Upload File

{{end}}

Perfect!

Now let’s go through the code in this file.

First, we have defined the template with the name upload, this is the name we will use to reference it later in our route handlers.

We then have some boilerplate HTML code in the head section, but I have included two important libraries here (well, just one is really important, the other is just for CSS vibes).

The HTMX library has been included with the

”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 使用 HTMX 和 Golang 上传文件

使用 HTMX 和 Golang 上传文件

发布于2024-11-04
浏览:908

Ofcourse you have already heard about the awesomeness of HTMX (you haven’t? well, good thing you’re here ?)

Today, we will be combining the simplicity of HTMX with the power of Golang to upload files to our server. Yeah, we are going to be building another exciting web feature with HTMX and Go.

By the way, if you really want a practical project-based guide on building fullstack apps with HTMX, check out my HTMX Go: Build Fullstack Applications with Golang and HTMX course [Discount Included].

So, let’s begin.

Setting Up the Go Project

The first step is to setup a simple Go project. We can do that by creating a folder, going into it and initialising it as a Go project using the commands below:

mkdir go-htmx-file-uploads
cd go-htmx-file-uploads
go mod init go-htmx-file-uploads

Once we have the project initialized, let’s now install some dependencies we will be requiring within our project.

This will be a simple server that will contain a single page with our upload form and also an endpoint that will be used to upload the file.

For routing, we will be using the Gorilla Mux routing library, but feel free to use any routing solution of your choice. We will also be using Google’s UUID library for Go to generate random names for our files when we upload them. This is a personal preference as you can generate file names in different ways.

Install these two with the commands below:

Gorillla Mux

go get -u github.com/gorilla/mux 

Google UUID

go get github.com/google/uuid

With these two installed, our project is fully set up, and we can move to the next step.

Creating our Templates

We will be creating two HTML templates for this little project.

The first template will be an HTML fragment that simply takes a slice of string messages that we can send from the server to the client.

This fragment will take this slice of messages and loop through it to create an HTML list to be returned to the client (remember how HTMX works with Hypermedia APIs, pretty cool huh ?).

So, let’s create that first.

At the root of the Go project, first create a templates folder inside which we will be storing all our templates.

Next, create a file messages.html inside the templates folder and add the following code to it:

{{define "messages"}}
    {{range .}}
  • {{ . }}
  • {{end}}
{{end}}

This defines a messages template and loops through the incoming slice of string messages to form an HTML list.

For our next template, we will be creating the file upload page itself.

Inside the templates folder, create a new file upload.html and paste in the code below:

{{define "upload"}}



    
    
    
    
    Upload File



    

Upload File

{{end}}

Perfect!

Now let’s go through the code in this file.

First, we have defined the template with the name upload, this is the name we will use to reference it later in our route handlers.

We then have some boilerplate HTML code in the head section, but I have included two important libraries here (well, just one is really important, the other is just for CSS vibes).

The HTMX library has been included with the

Then I have also brought in the Bootstrap CSS library, this is just to give our page elements some nice styling. It is not compulsory for this demo.

In the page itself, we have a form that does the upload. Let’s break down what is within the

tags.

First we have a

with an id of messages , this is the container where we will be loading all our server messages that come in as HTML. Remember the messages template, yeah, this is where the list of messages will be going into.

After that, we have the form input element that is set to file to ensure that it displays a file upload widget. We have given it the name avatar to reference it at the backend, but you can give this any name you want. I gave it avatar because i was using it to upload profile images.

Finally, we have the button which has been supercharged with HTMX. I have displayed it again below so we can go through it


First, I have added hx-post="/upload" , this tells it to submit the form to the /upload endpoint that we will be creating very soon and will handle the file upload.

Next is hx-encoding="multipart/form-data", this is compulsory for uploading files with HTMX to let the server know you’re sending along a file with the request.

Then we have hx-target="#messages" which tells the button to insert any response from the server into the

with an id of messages.

These three define our configuration for uploading the file to our backend.

Below is a preview of what our page looks like:

File Uploads with HTMX and Golang

Processing the File Upload

Now that we have our templates, it’s time to write the code that will display our upload page and also handle our file uploads.

To begin, at the root of the Go project, create a uploads folder. This is the folder where all our uploaded files will be stored.

With that in place, let’s write our main file.

Create the file main.go at the root of your project and add the following code:

package main

import (
    "html/template"
    "log"
    "net/http"
    "io"
    "os"
    "path/filepath"
    "github.com/google/uuid"
    "github.com/gorilla/mux"
)

var tmpl *template.Template

func init(){
    tmpl, _ = template.ParseGlob("templates/*.html")
}

func main() {


    router := mux.NewRouter()

    router.HandleFunc("/", homeHandler).Methods("GET")

    router.HandleFunc("/upload", UploadHandler).Methods("POST")

    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", router))
}

func homeHandler(w http.ResponseWriter, r *http.Request) {

    tmpl.ExecuteTemplate(w, "upload", nil)

}

func UploadHandler(w http.ResponseWriter, r *http.Request) {


        // Initialize error messages slice
        var serverMessages []string

        // Parse the multipart form, 10 MB max upload size
        r.ParseMultipartForm(10  0 {
                tmpl.ExecuteTemplate(w, "messages", serverMessages)
                return
            }

        }
        defer file.Close()

        // Generate a unique filename to prevent overwriting and conflicts
        uuid, err := uuid.NewRandom()
        if err != nil {
            serverMessages = append(serverMessages, "Error generating unique identifier")
            tmpl.ExecuteTemplate(w, "messages", serverMessages)

            return
        }
        filename := uuid.String()   filepath.Ext(handler.Filename) // Append the file extension

        // Create the full path for saving the file
        filePath := filepath.Join("uploads", filename)

        // Save the file to the server
        dst, err := os.Create(filePath)
        if err != nil {
            serverMessages = append(serverMessages, "Error saving the file")
            tmpl.ExecuteTemplate(w, "messages", serverMessages)

            return
        }
        defer dst.Close()
        if _, err = io.Copy(dst, file); err != nil {
            serverMessages = append(serverMessages, "Error saving the file")
            tmpl.ExecuteTemplate(w, "messages", serverMessages)
            return
        }


        serverMessages = append(serverMessages, "File Successfully Saved")
        tmpl.ExecuteTemplate(w, "messages", serverMessages)


}

Yope, that’s a bunch of code. Don’t worry, we’ll go through it all step by step to figure out what this is all doing.

First we define our package main and import a bunch of libraries we will be making use of. These imports include the Gorilla mux router and the Google UUID library that we installed earlier.

After that, I create a global tmpl variable to hold all the HTML templates in the project and in the init() function, the templates are all loaded from the templates folder.

The main() Function

Now to the main() function. Here, we have initlialized the Gorilla Mux router and set up two routes.

The GET / base route which will be handled by a homeHandler function and displays our upload form, and the POST /upload route that will be handled by UploadHandler and handles the upload itself.

Finally, we print out a message to indicate that our server is running, and run the server on port 8080.

The Handler Functions

First we have homeHandler . This is the function that handles our base route, and it simply calls ExecuteTemplate on the tmpl variable with the name we gave to our template

tmpl.ExecuteTemplate(w, "upload", nil)

This call is enough to simply render our upload page to the screen when we visit the base route.

After that is the UploadHandler function. This is where the real magic happens, so let’s walk through the function.

First, we create a slice of strings called serverMessages to hold any message we want to send back to the client.

After that, we call ParseMultipartForm on the request pointer to limit the size of uploaded files to within 20MB.

r.ParseMultipartForm(10 



Next, we get a hold on our file by referencing the name of the file field with FormFile on the request pointer.

With our reference to the file, we check if there is actually a file, and if not, we return a message saying that no file was submitted or an error was encountered when trying to retrieve the file to account for other errors.

file, handler, err := r.FormFile("avatar")
        if err != nil {
            if err == http.ErrMissingFile {
                serverMessages = append(serverMessages, "No file submitted")
            } else {
                serverMessages = append(serverMessages, "Error retrieving the file")
            }

            if len(serverMessages) > 0 {
                tmpl.ExecuteTemplate(w, "messages", serverMessages)
                return
            }

        }

At this point, if our messages slice is not empty, we return the messages to the client and exit the function.

If a file is found, we keep the file open and move to generating a new name for it with the UUID library and also handle the errors in that process accordingly.

We build a new file name with the generated string and the file extension and set it’s path to the uploads folder.

    uuid, err := uuid.NewRandom()
        if err != nil {
            serverMessages = append(serverMessages, "Error generating unique identifier")
            tmpl.ExecuteTemplate(w, "messages", serverMessages)

            return
        }
        filename := uuid.String()   filepath.Ext(handler.Filename) 

        // Create the full path for saving the file
        filePath := filepath.Join("uploads", filename)

Once the new file path is constructed, we then use the os library to create the file path

After that, we use the io library to move the file from it’s temporary location to the new location and also handle errors accordingly.

    dst, err := os.Create(filePath)
        if err != nil {
            serverMessages = append(serverMessages, "Error saving the file")
            tmpl.ExecuteTemplate(w, "messages", serverMessages)

            return
        }
        defer dst.Close()
        if _, err = io.Copy(dst, file); err != nil {
            serverMessages = append(serverMessages, "Error saving the file")
            tmpl.ExecuteTemplate(w, "messages", serverMessages)
            return
        }

If we get no errors from the file saving process, we then return a successful message to the client using our messages template as we have done with previous messages.

serverMessages = append(serverMessages, "File Successfully Saved")
tmpl.ExecuteTemplate(w, "messages", serverMessages)

And that’s everything.

Now let’s take this code for a spin.

Testing the File Upload

Save the file and head over to the command line.

At the root of the project, use the command below to run our little file upload application:

go run main.go

Now go to your browser and head over to http://localhost:8080, you should see the upload screen displayed.

Try testing with no file to see the error message displayed. Then test with an actual file and also see that you get a successful message.

Check the uploads folder to confirm that the file is actually being saved there.

File Uploads with HTMX and Golang

And Wholla! You can now upload files to your Go servers using HTMX.

Conclusion

If you have enjoyed this article, and will like to learn more about building projects with HTMX, I’ll like you to check out HTMX Go: Build Fullstack Applications with Golang and HTMX, and The Complete HTMX Course: Zero to Pro with HTMX to further expand your knowledge on building hypermedia-driven applications with HTMX.

版本声明 本文转载于:https://dev.to/coderonfleek/file-uploads-with-htmx-and-golang-57ad?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 在Ubuntu/linux上安装mysql-python时,如何修复\“ mysql_config \”错误?
    在Ubuntu/linux上安装mysql-python时,如何修复\“ mysql_config \”错误?
    mysql-python安装错误:“ mysql_config找不到”“ 由于缺少MySQL开发库而出现此错误。解决此问题,建议在Ubuntu上使用该分发的存储库。使用以下命令安装Python-MysqldB: sudo apt-get安装python-mysqldb sudo pip in...
    编程 发布于2025-07-03
  • Spark DataFrame添加常量列的妙招
    Spark DataFrame添加常量列的妙招
    在Spark Dataframe ,将常数列添加到Spark DataFrame,该列具有适用于所有行的任意值的Spark DataFrame,可以通过多种方式实现。使用文字值(SPARK 1.3)在尝试提供直接值时,用于此问题时,旨在为此目的的column方法可能会导致错误。 df.withCo...
    编程 发布于2025-07-03
  • 在Python中如何创建动态变量?
    在Python中如何创建动态变量?
    在Python 中,动态创建变量的功能可以是一种强大的工具,尤其是在使用复杂的数据结构或算法时,Dynamic Variable Creation的动态变量创建。 Python提供了几种创造性的方法来实现这一目标。利用dictionaries 一种有效的方法是利用字典。字典允许您动态创建密钥并分...
    编程 发布于2025-07-03
  • 同实例无需转储复制MySQL数据库方法
    同实例无需转储复制MySQL数据库方法
    在同一实例上复制一个MySQL数据库而无需转储在同一mySQL实例上复制数据库,而无需创建InterMediate sqql script。以下方法为传统的转储和IMPORT过程提供了更简单的替代方法。 直接管道数据 MySQL手动概述了一种允许将mysqldump直接输出到MySQL clie...
    编程 发布于2025-07-03
  • 如何在Java中正确显示“ DD/MM/YYYY HH:MM:SS.SS”格式的当前日期和时间?
    如何在Java中正确显示“ DD/MM/YYYY HH:MM:SS.SS”格式的当前日期和时间?
    如何在“ dd/mm/yyyy hh:mm:mm:ss.ss”格式“ gormat 解决方案: args)抛出异常{ 日历cal = calendar.getInstance(); SimpleDateFormat SDF =新的SimpleDateFormat(“...
    编程 发布于2025-07-03
  • 为什么PYTZ最初显示出意外的时区偏移?
    为什么PYTZ最初显示出意外的时区偏移?
    与pytz 最初从pytz获得特定的偏移。例如,亚洲/hong_kong最初显示一个七个小时37分钟的偏移: 差异源利用本地化将时区分配给日期,使用了适当的时区名称和偏移量。但是,直接使用DateTime构造器分配时区不允许进行正确的调整。 example pytz.timezone(...
    编程 发布于2025-07-03
  • 您可以使用CSS在Chrome和Firefox中染色控制台输出吗?
    您可以使用CSS在Chrome和Firefox中染色控制台输出吗?
    在javascript console 中显示颜色是可以使用chrome的控制台显示彩色文本,例如红色的redors,for for for for错误消息?回答是的,可以使用CSS将颜色添加到Chrome和Firefox中的控制台显示的消息(版本31或更高版本)中。要实现这一目标,请使用以下模...
    编程 发布于2025-07-03
  • JavaScript计算两个日期之间天数的方法
    JavaScript计算两个日期之间天数的方法
    How to Calculate the Difference Between Dates in JavascriptAs you attempt to determine the difference between two dates in Javascript, consider this s...
    编程 发布于2025-07-03
  • Java字符串非空且非null的有效检查方法
    Java字符串非空且非null的有效检查方法
    检查字符串是否不是null而不是空的 if(str!= null && str.isementy())二手: if(str!= null && str.length()== 0) option 3:trim()。isement(Isement() trim whitespace whitesp...
    编程 发布于2025-07-03
  • 如何检查对象是否具有Python中的特定属性?
    如何检查对象是否具有Python中的特定属性?
    方法来确定对象属性存在寻求一种方法来验证对象中特定属性的存在。考虑以下示例,其中尝试访问不确定属性会引起错误: >>> a = someClass() >>> A.property Trackback(最近的最新电话): 文件“ ”,第1行, AttributeError: SomeClass...
    编程 发布于2025-07-03
  • Python高效去除文本中HTML标签方法
    Python高效去除文本中HTML标签方法
    在Python中剥离HTML标签,以获取原始的文本表示Achieving Text-Only Extraction with Python's MLStripperTo streamline the stripping process, the Python standard librar...
    编程 发布于2025-07-03
  • 在程序退出之前,我需要在C ++中明确删除堆的堆分配吗?
    在程序退出之前,我需要在C ++中明确删除堆的堆分配吗?
    在C中的显式删除 在C中的动态内存分配时,开发人员通常会想知道是否有必要在heap-procal extrable exit exit上进行手动调用“ delete”操作员,但开发人员通常会想知道是否需要手动调用“ delete”操作员。本文深入研究了这个主题。 在C主函数中,使用了动态分配变量(H...
    编程 发布于2025-07-03
  • Go web应用何时关闭数据库连接?
    Go web应用何时关闭数据库连接?
    在GO Web Applications中管理数据库连接很少,考虑以下简化的web应用程序代码:出现的问题:何时应在DB连接上调用Close()方法?,该特定方案将自动关闭程序时,该程序将在EXITS EXITS EXITS出现时自动关闭。但是,其他考虑因素可能保证手动处理。选项1:隐式关闭终止数...
    编程 发布于2025-07-03
  • 如何使用Python有效地以相反顺序读取大型文件?
    如何使用Python有效地以相反顺序读取大型文件?
    在python 中,如果您使用一个大文件,并且需要从最后一行读取其内容,则在第一行到第一行,Python的内置功能可能不合适。这是解决此任务的有效解决方案:反向行读取器生成器 == ord('\ n'): 缓冲区=缓冲区[:-1] ...
    编程 发布于2025-07-03

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3