使用 HTMX 和 Golang 上傳文件

WBOY
發布: 2024-08-20 06:32:41
原創
417 人瀏覽過

當然,您已經聽說過 HTMX 的強大之處(您還沒聽說過嗎?好吧,幸好您在這裡?)

今天,我們將結合 HTMX 的簡單性和 Golang 的強大功能來將檔案上傳到我們的伺服器。是的,我們將使用 HTMX 和 Go 來建立另一個令人興奮的 Web 功能。

順便說一句,如果您確實想要一本關於使用 HTMX 建立全端應用程式的基於專案的實用指南,請查看我的 HTMX + Go:使用 Golang 和 HTMX 建立全端應用程式課程 [含折扣]。

那麼,讓我們開始吧。

設定 Go 項目

第一步是設定一個簡單的 Go 項目。我們可以透過建立一個資料夾,進入其中並使用以下命令將其初始化為 Go 專案來做到這一點:

雷雷

初始化專案後,現在讓我們安裝專案中需要的一些依賴項。

這將是一個簡單的伺服器,其中包含帶有我們的上傳表單的單一頁面以及用於上傳檔案的端點。

對於路由,我們將使用 Gorilla Mux 路由庫,但請隨意使用您選擇的任何路由解決方案。我們還將使用 Google 的 Go 的 UUID 庫在上傳文件時為文件產生隨機名稱。這是個人喜好,因為您可以透過不同的方式產生檔案名稱。

使用以下指令安裝這兩個:

大猩猩 Mux

雷雷

Google UUID

雷雷

安裝這兩個後,我們的專案就完全建立起來了,我們可以進入下一步了。

創建我們的模板

我們將為這個小專案建立兩個 HTML 範本。

第一個範本將是一個 HTML 片段,它只需要我們可以從伺服器傳送到客戶端的字串訊息片段。

此片段將獲取此訊息片段並循環遍歷它以建立要傳回給客戶端的 HTML 清單(還記得 HTMX 如何與超媒體 API 配合使用,很酷吧?)。

所以,讓我們先創建它。

在 Go 專案的根目錄下,首先建立一個 templates 資料夾,我們將在其中儲存所有範本。

接下來,在 templates 資料夾中建立一個檔案 messages.html 並加入以下程式碼:

雷雷

這定義了一個訊息模板,並循環傳入的字串訊息片段以形成 HTML 清單。

對於我們的下一個模板,我們將建立文件上傳頁面本身。

在 templates 資料夾中,建立一個新檔案 upload.html 並貼上以下程式碼:

雷雷

完美!

現在我們來看看這個檔案中的程式碼。

首先,我們定義了名為 upload 的模板,這是我們稍後在路由處理程序中引用它的名稱。

然後我們在 head 部分有一些樣板 HTML 程式碼,但我在這裡包含了兩個重要的函式庫(好吧,只有一個非常重要,另一個僅用於 CSS 共鳴)。

HTMX 庫已包含在

然後我還引入了 Bootstrap CSS 庫,這只是為了給我們的頁面元素一些漂亮的樣式。此演示不是強制性的。

在頁面本身中,我們有一個用於上傳的表單。讓我們分解一下

中的內容:標籤.

首先我們有一個

; id 為 messages ,這是我們將載入所有以 HTML 形式出現的伺服器訊息的容器。記住訊息模板,是的,這就是訊息清單將進入的地方。

之後,我們將表單輸入元素設為 file 以確保它顯示檔案上傳小工具。我們已為其指定名稱 avatar 以在後端引用它,但您可以為其指定任何名稱。我給它頭像是因為我用它來上傳個人資料圖片。

最後,我們有了經過 HTMX 增強的按鈕。我在下面再次展示了它,以便我們可以瀏覽一下

雷雷

首先,我新增了 hx-post="/upload" ,這告訴它將表單提交到我們很快就會建立的 /upload 端點,並將處理檔案上傳。

接下來是 hx-encoding="multipart/form-data",這是使用 HTMX 上傳檔案所必需的,以便讓伺服器知道您正在隨請求發送檔案。

然後我們有 hx-target="#messages" 告訴按鈕將來自伺服器的任何回應插入到

中帶有訊息 ID。

這三個定義了我們將檔案上傳到後端的設定。

下面是我們頁面的預覽:

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 << 20) // Retrieve the file from form data 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 } } 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 << 20)
登入後複製

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 outHTMX + Go: Build Fullstack Applications with Golang and HTMX, andThe Complete HTMX Course: Zero to Pro with HTMXto further expand your knowledge on building hypermedia-driven applications with HTMX.

以上是使用 HTMX 和 Golang 上傳文件的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!