?Go?語言實現(xiàn)?HTTP?文件上傳和下載
前言:
近我使用 Go 語言完成了一個正式的 Web 應用,有一些方面的問題在使用 Go 開發(fā) Web 應用過程中比較重要。過去,我將 Web 開發(fā)作為一項職業(yè)并且把使用不同的語言和范式開發(fā) Web 應用作為一項愛好,因此對于 Web 開發(fā)領域有一些心得體會。
總的來說,我喜歡使用 Go 語言進行 Web 開發(fā),盡管開始一段時間需要去適應它。Go 語言有一些坑,但是正如本篇文章中所要討論的文件上傳與下載,Go 語言的標準庫與內(nèi)置函數(shù),使得開發(fā)是種愉快的體驗。
在接下來的幾篇文章中,我將重點討論我在 Go 中編寫生產(chǎn)級 Web 應用程序時遇到的一些問題,特別是關于身份驗證/授權(quán)的問題。
這篇文章將展示 HTTP 文件上傳和下載的基本示例。我們將一個有 type 文本框和一個 uploadFile 上傳框的 HTML 表單作為客戶端。
讓我們來看下 Go 語言中是如何解決這種在 Web 開發(fā)中隨處可見的問題的。
代碼示例 首先,我們在服務器端設定兩個路由, /upload 用于文件上傳, /files/* 用于文件下載。
const maxUploadSize = 2 * 1024 * 2014 // 2 MB const uploadPath = "./tmp" func main() { http.HandleFunc("/upload", uploadFileHandler()) fs := http.FileServer(http.Dir(uploadPath)) http.Handle("/files/", http.StripPrefix("/files", fs)) log.Print("Server started on localhost:8080, use /upload for uploading files and /files/{fileName} for downloading files.") log.Fatal(http.ListenAndServe(":8080", nil)) }
我們還將要上傳的目標目錄,以及我們接受的最大文件大小定義為常量。注意這里,整個文件服務的概念是如此的簡單 —— 我們僅使用標準庫中的工具,使用 http.FileServe 創(chuàng)建一個 HTTP 處理程序,它將使用 http.Dir(uploadPath) 提供的目錄來上傳文件。
現(xiàn)在我們只需要實現(xiàn) uploadFileHandler 。
這個處理程序?qū)韵鹿δ埽?/strong>
- 驗證文件最大值
- 從請求驗證文件和 POST 參數(shù)
- 檢查所提供的文件類型(我們只接受圖像和 PDF)
- 創(chuàng)建一個隨機文件名
- 將文件寫入硬盤
- 處理所有錯誤,如果一切順利返回成功消息
第一步,我們定義處理程序:
func uploadFileHandler() http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
然后,我們使用 http.MaxBytesReader 驗證文件大小,當文件大小大于設定值時它將返回一個錯誤。錯誤將被一個助手程序 renderError 進行處理,它返回錯誤信息及對應的 HTTP 狀態(tài)碼。
r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize) if err := r.ParseMultipartForm(maxUploadSize); err != nil { renderError(w, "FILE_TOO_BIG", http.StatusBadRequest) return }
如果文件大小驗證通過,我們將檢查并解析表單參數(shù)類型和上傳的文件,并讀取文件。在本例中,為了清晰起見,我們不使用花哨的 io.Reader 和 io.Writer 接口,我們只是簡單的將文件讀取到一個字節(jié)數(shù)組中,這點我們后面會寫到。
fileType := r.PostFormValue("type") file, _, err := r.FormFile("uploadFile") if err != nil { renderError(w, "INVALID_FILE", http.StatusBadRequest) return } defer file.Close() fileBytes, err := ioutil.ReadAll(file) if err != nil { renderError(w, "INVALID_FILE", http.StatusBadRequest) return }
現(xiàn)在我們成功的驗證了文件的大小,并且讀取了文件,接下來我們該檢驗文件的類型了。一種廉價但是并不安全的方式,只檢查文件擴展名,并相信用戶沒有改變它,但是對于一個正式的項目來講不應該這么做。
幸運的是,Go 標準庫提供給我們一個 http.DetectContentType 函數(shù),這個函數(shù)基于 mimesniff 算法,只需要讀取文件的前 512 個字節(jié)就能夠判定文件類型。
filetype := http.DetectContentType(fileBytes) if filetype != "image/jpeg" && filetype != "image/jpg" && filetype != "image/gif" && filetype != "image/png" && filetype != "application/pdf" { renderError(w, "INVALID_FILE_TYPE", http.StatusBadRequest) return }
在實際應用程序中,我們可能會使用文件元數(shù)據(jù)做一些事情,例如將其保存到數(shù)據(jù)庫或?qū)⑵渫扑偷酵獠糠?mdash;—以任何方式,我們將解析和操作元數(shù)據(jù)。這里我們創(chuàng)建一個隨機的新名字(這在實踐中可能是一個 UUID)并將新文件名記錄下來。
fileName := randToken(12) fileEndings, err := mime.ExtensionsByType(fileType) if err != nil { renderError(w, "CANT_READ_FILE_TYPE", http.StatusInternalServerError) return } newPath := filepath.Join(uploadPath, fileName+fileEndings[0]) fmt.Printf("FileType: %s, File: %s\n", fileType, newPath)
馬上就大功告成了,只剩下一個關鍵步驟-寫文件。如上文所提到的,我們只需要復制讀取的二進制文件到一個新創(chuàng)建的名為 newFile 的文件處理程序里。
如果所有部分都沒問題,我們給用戶返回一個 SUCCESS 信息。
newFile, err := os.Create(newPath) if err != nil { renderError(w, "CANT_WRITE_FILE", http.StatusInternalServerError) return } defer newFile.Close() if _, err := newFile.Write(fileBytes); err != nil { renderError(w, "CANT_WRITE_FILE", http.StatusInternalServerError) return } w.Write([]byte("SUCCESS"))
這樣可以了. 你可以對這個簡單的例子進行測試,使用虛擬的文件上傳 HTML 頁面,cURL 或者工具例如 postman [1] 。
這里是完整的代碼示例 這里 [2]
結(jié)論 這是又一個證明了 Go 如何允許用戶為 Web 編寫簡單而強大的軟件,而不必像處理其他語言和生態(tài)系統(tǒng)中固有的無數(shù)抽象層。
在接下來的篇幅中,我將展示一些在我第一次使用 Go 語言編寫正式的 Web 應用中其他細節(jié),敬請期待。;)
// 根據(jù) reddit 用戶 lstokeworth 的反饋對部分代碼進行了修改。謝謝:)
到此這篇關于 Go 語言完成 HTTP 文件上傳和下載的文章就介紹到這了,更多相關 Go HTTP 文件上傳內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Go 庫bytes.Buffer和strings.Builder使用及性能對比
這篇文章主要為大家介紹了Go 庫bytes.Buffer和strings.Builder使用及性能對比,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12Golang簡單實現(xiàn)http的server端和client端
Http 服務是基于 Tcp 的應用層的實現(xiàn),也是最常見的網(wǎng)絡協(xié)議之一。本文主要介紹了Golang簡單實現(xiàn)http的server端和client端,感興趣的可以了解一下2021-06-06golang中拿slice當queue和拿list當queue使用分析
這篇文章主要為大家介紹了golang?中拿slice當queue和拿list當queue使用分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08詳解Golang如何實現(xiàn)一個環(huán)形緩沖器
環(huán)形緩沖器(ringr?buffer)是一種用于表示一個固定尺寸、頭尾相連的緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu),適合緩存數(shù)據(jù)流。本文將利用Golang實現(xiàn)一個環(huán)形緩沖器,需要的可以參考一下2022-09-09解決golang處理http response碰到的問題和需要注意的點
這篇文章主要介紹了解決golang處理http response碰到的問題和需要注意的點,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12