Golang使用Gin實(shí)現(xiàn)文件上傳的示例代碼
前言
今天我們來(lái)聊聊如何使用Gin實(shí)現(xiàn)文件上傳。
Go標(biāo)準(zhǔn)庫(kù)net/http對(duì)文件上傳已經(jīng)提供了非常完善的支持,而Gin框架在其基礎(chǔ)上進(jìn)一步封裝,因此使用Gin開發(fā)文件上傳功能時(shí),只需要簡(jiǎn)單幾行代碼便可以實(shí)現(xiàn),Gin框架支持單個(gè)文件與多個(gè)文件同時(shí)上傳。
使用原生net/http庫(kù)實(shí)現(xiàn)文件上傳
我們首先看看實(shí)現(xiàn)一個(gè)HTTP服務(wù)器,提供文件上傳功能的簡(jiǎn)單示例
package main import ( "io" "io/ioutil" "log" "net/http" "github.com/julienschmidt/httprouter" ) const ( MAX_UPLOAD_SIZE = 1024 * 1024 * 20 //最大上傳大小,50MB ) func main() { r := RegisterHandlers() http.ListenAndServe(":8080", r) // 開啟一個(gè)http服務(wù) } // 定義路由 func RegisterHandlers() *httprouter.Router { router := httprouter.New() router.POST("/upload", uploadHandler) return router } // 文件上傳接口 func uploadHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) { r.Body = http.MaxBytesReader(w, r.Body, MAX_UPLOAD_SIZE) if err := r.ParseMultipartForm(MAX_UPLOAD_SIZE); err != nil { log.Printf("File is too big") return } file, headers, err := r.FormFile("file") if err != nil { log.Printf("Error when try to get file: %v", err) return } //獲取上傳文件的類型 if headers.Header.Get("Content-Type") != "image/png" { log.Printf("只允許上傳png圖片") return } data, err := ioutil.ReadAll(file) if err != nil { log.Printf("Read file error: %v", err) return } fn := headers.Filename err = ioutil.WriteFile("./video/"+fn, data, 0666) if err != nil { log.Printf("Write file error: %v", err) return } w.WriteHeader(http.StatusCreated) io.WriteString(w, "Uploaded successfully") }
如上我們通過(guò)r.FormFile函數(shù)獲取上傳的文件對(duì)象,以及文件的相關(guān)信息。然后通過(guò)headers.Header.Get函數(shù)獲取上傳文件的類型,判斷類型是否符合要求。接著,使用ioutil.ReadAll函數(shù)讀取文件的內(nèi)容,并將其存儲(chǔ)在data變量中。然后,通過(guò)headers.Filename獲取上傳文件的文件名,并使用ioutil.WriteFile函數(shù)將文件內(nèi)容寫入到指定路徑下的文件中。
使用Gin實(shí)現(xiàn)文件上傳
單個(gè)文件上傳
FormFile()獲取文件
單文件上傳使用gin.Context的FormFile()方法,該方法的值為POST請(qǐng)求中文件上傳字段的名稱:
engine := gin.Default() engine.POST("/upload", func(c *gin.Context) { file, err := c.FormFile("file") }) engine.Run()
SaveUploadedFile()保存到本地
調(diào)用gin.Context的SaveUploadedFile()方法可以將文件保存到某個(gè)目錄下:
dst := "./uploads/" + file.Filename c.SaveUploadedFile(file,"./uploadFile")
設(shè)置緩沖區(qū)大小
Go默認(rèn)文件上傳緩沖區(qū)為32M,當(dāng)有大量文件上傳時(shí),服務(wù)器內(nèi)存的壓力會(huì)很大,因此可以通過(guò)MaxMultipartMemory屬性來(lái)設(shè)置緩沖區(qū)大小:
//8M engine.MaxMultipartMemory = 8 << 20
限制文件大小
上傳文件時(shí),不限制文件大小可以會(huì)導(dǎo)致服務(wù)存儲(chǔ)空間暴漲,因?yàn)橛斜仨毾拗粕蟼魑募笮。?/p>
fileMaxSize := 4 << 20 //4M if int(file.Size) > fileMaxSize { c.String(http.StatusBadRequest, "文件不允許大小于4M") return }
限制文件類型
對(duì)文件類型,也可以進(jìn)行限制:
reader, err := file.Open() if err != nil { fmt.Println(err) return } b := make([]byte, 512) reader.Read(b) ? contentType := http.DetectContentType(b) if contentType != "image/jpeg" && contentType != "image/png" { c.String(http.StatusOK, "文件格式錯(cuò)誤") return }
在上面的代碼中,我們讀取文件的前512個(gè)字節(jié),再調(diào)用http.DetectContentType()便可以獲取文件的MIME值。
完整示例
package main ? import ( "fmt" "log" "net/http" ? "github.com/gin-gonic/gin" ) ? func main() { engine := gin.Default() //8M engine.MaxMultipartMemory = 8 << 20 engine.POST("/upload", func(c *gin.Context) { file, err := c.FormFile("file") if err != nil { log.Println(err) c.String(http.StatusBadRequest, "文件上傳失敗") return } fileMaxSize := 4 << 20 //4M if int(file.Size) > fileMaxSize { c.String(http.StatusBadRequest, "文件不允許大小于32KB") return } ? reader, err := file.Open() if err != nil { fmt.Println(err) return } b := make([]byte, 512) reader.Read(b) ? contentType := http.DetectContentType(b) if contentType != "image/jpeg" && contentType != "image/png" { c.String(http.StatusOK, "文件格式錯(cuò)誤") return } ? dst := "./uploads/" + file.Filename c.SaveUploadedFile(file, dst) c.String(http.StatusOK, fmt.Sprintf("'%s' 上傳成功!", file.Filename)) }) engine.Run() } ?
測(cè)試文件上傳
$ curl -F "file=@./1.jpg" -X POST "http://localhost:8080/upload" '1.jpg' 上傳成功!
多個(gè)文件上傳
MultipartForm()獲取多個(gè)文件
如果要上傳多個(gè)文件,多次調(diào)用gin.Context的FormFile()方法也是可以的,但更好的方式是使用gin.Context的MultipartForm()方法:
package main ? import ( "fmt" "log" "net/http" ? "github.com/gin-gonic/gin" ) ? func main() { engine := gin.Default() engine.POST("/uploadMul", func(c *gin.Context) { form, err := c.MultipartForm() if err != nil { log.Println(err) c.String(http.StatusBadRequest, "文件上傳失敗") return } files := form.File["upload"] for _, file := range files { fmt.Println(file.Filename) } c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files))) }) engine.Run() }
測(cè)試文件上傳
運(yùn)行程序后,使用curl命令上傳多個(gè)文件:
$ curl -F "upload=@./1.jpg" -F "upload=@./2.jpg" -X POST "http://localhost:8080/uploadMul 2 files uploaded
總結(jié)
Go標(biāo)準(zhǔn)庫(kù)net/http對(duì)文件上傳已經(jīng)提供了非常完善的支持,可以滿足我們大部分需求,Gin框架在其基礎(chǔ)做了封裝,讓我們使用起來(lái)更加方便迅速。
以上就是Golang使用Gin實(shí)現(xiàn)文件上傳的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Golang Gin文件上傳的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go中map數(shù)據(jù)類型3點(diǎn)小知識(shí)
這篇文章主要介紹了Go中map數(shù)據(jù)類型3點(diǎn)小知識(shí),在go中使用map時(shí)可能會(huì)用本文中的小知識(shí),需要的朋友可以參考下2014-10-10golang中range在slice和map遍歷中的注意事項(xiàng)
今天小編就為大家分享一篇關(guān)于golang中range在slice和map遍歷中的注意事項(xiàng),小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-04-04Go語(yǔ)言學(xué)習(xí)函數(shù)+結(jié)構(gòu)體+方法+接口
這篇文章主要介紹了Go語(yǔ)言學(xué)習(xí)函數(shù)+結(jié)構(gòu)體+方法+接口,文章圍繞主題的相關(guān)資料展開詳細(xì)的文章說(shuō)明,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-05-05go語(yǔ)言題解LeetCode1299將每個(gè)元素替換為右側(cè)最大元素
這篇文章主要為大家介紹了go語(yǔ)言LeetCode刷題1299將每個(gè)元素替換為右側(cè)最大元素示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01golang數(shù)組-----尋找數(shù)組中缺失的整數(shù)方法
這篇文章主要介紹了golang數(shù)組-----尋找數(shù)組中缺失的整數(shù)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12golang struct擴(kuò)展函數(shù)參數(shù)命名警告解決方法
今天在使用VSCode編寫golang代碼時(shí),定義一個(gè)struct,擴(kuò)展幾個(gè)方法,需要的朋友可以參考下2017-02-02golang在GRPC中設(shè)置client的超時(shí)時(shí)間
這篇文章主要介紹了golang在GRPC中設(shè)置client的超時(shí)時(shí)間,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04golang 定時(shí)任務(wù)方面time.Sleep和time.Tick的優(yōu)劣對(duì)比分析
這篇文章主要介紹了golang 定時(shí)任務(wù)方面time.Sleep和time.Tick的優(yōu)劣對(duì)比分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05