golang web服务有时候需要提供上传文件的接口,以下就是具体示例。为了示例简单(吐槽下 golang 的错误处理),忽略了所有的错误处理。本文会用两种方式(标准库和gin
)详细讲解 golang 实现文件上传的实现。
gin是一个用 golang 实现的优秀 web 服务框架
上传文件 标准包实现package mainimport ( "io" "log" "net/http" "os")var ( // 文件 key uploadfileKey = "upload-key")func main() { http.HandleFunc("/upload",uploadHandler) if err := http.ListenAndServe(":8080",nil); err != nil { log.Fatalf("error to start http server:%s",err.Error()) }}func uploadHandler(w http.ResponseWriter,r *http.Request) { // 接受文件 file,header,err := r.Formfile(uploadfileKey) if err != nil { // ignore the error handler } log.Printf("selected file name is %s",header.filename) // 将文件拷贝到指定路径下,或者其他文件 *** 作 dst,err := os.Create(header.filename) if err != nil { // ignore } _,err = io.copy(dst,file) if err != nil { // ignore }}Gin 实现
package mainimport ( "github.com/gin-gonic/gin")var ( uploadfileKey = "upload-key")func main() { r := gin.Default() r.POST("/upload",uploadHandler) r.Run()}func uploadHandler(c *gin.Context) { header,err := c.Formfile(uploadfileKey) if err != nil { //ignore } dst := header.filename // gin 简单做了封装,拷贝了文件流 if err := c.SaveUploadedfile(header,dst); err != nil { // ignore }}
SaveUploadedfile 实现如下:
// SaveUploadedfile uploads the form file to specific dst.func (c *Context) SaveUploadedfile(file *multipart.fileheader,dst string) error { src,err := file.open() if err != nil { return err } defer src.Close() //创建 dst 文件 out,err := os.Create(dst) if err != nil { return err } defer out.Close()// 拷贝文件 _,err = io.copy(out,src) return err}上传文件和参数
有时候除了选中文件外,我们还需要向服务端传递一些参数。在 http multipart 请求格式中。值是以键值对的形式传递的。
标准包实现可以使用 Request
下的 MultipartForm
files :=r.MultipartForm.file
files 是 map[string][]*fileheader
类型,可以传递多个文件
values := r.MultipartForm.Value
values 是 map[string][]string
类型,可以允许有多个同名的变量,每个同名的变量值在一个切片中
gin
的 Context
中包含了*http.Request
,因此完全可以用与标准库相同的方式处理。同时 gin
对参数的获取也做了一层分装。
假设需要传递 name,age 以及 key 为 upload-key 的文件。首先定义结构体:
type newForm struct { UploadKey *multipart.fileheader `form:"upload-key"` name string `form:"name"` Age int `form:"age"`}
在获取 form 的时候直接使用 gin
分装的方法ShouldBind
获取到所有参数
var form newForm if err := c.ShouldBind(&form); err != nil{ //ignore }
同时newForm
中可以添加binding
tag 进行参数校验。具体可以参考 gin 的官方文档 gin 请求参数校验。
multipart form 的 clIEnt 写法示例
package mainimport ( "bytes" "fmt" "io" "log" "mime/multipart" "net/http" "os" "path/filepath")var ( uploadfileKey = "upload-key")func main() { url := "http://127.0.0.1:8080/upload" path := "/tmp/test.txt" params := map[string]string{ "key1": "val1",} req,err := NewfileUploadRequest(url,path,params) if err != nil { fmt.Printf("error to new upload file request:%s\n",err.Error()) return } clIEnt := &http.ClIEnt{} resp,err := clIEnt.Do(req) if err != nil { fmt.Printf("error to request to the server:%s\n",err.Error()) return } body := &bytes.Buffer{} _,err = body.ReadFrom(resp.Body) if err != nil { log.Fatal(err) } defer resp.Body.Close() fmt.Println(body)}// NewfileUploadRequest ...func NewfileUploadRequest(url,path string,params map[string]string) (*http.Request,error) { file,err := os.Open(path) if err != nil { return nil,err } defer file.Close() body := &bytes.Buffer{} // 文件写入 body writer := multipart.NewWriter(body) part,err := writer.CreateFormfile(uploadfileKey,filepath.Base(path)) if err != nil { return nil,err } _,err = io.copy(part,file) // 其他参数列表写入 body for k,v := range params { if err := writer.WriteFIEld(k,v); err != nil { return nil,err } } if err := writer.Close(); err != nil { return nil,err } req,err := http.NewRequest(http.MethodPost,url,body) if err != nil { return nil,err } req.header.Add("Content-Type",writer.FormDataContentType()) return req,err}总结
Gin 的实现方式更加简单高效,框架为我们封装了很多细节。使用起来更加方便。标准库实现相对而言也算简单。但是需要我们自己组织和校验请求参数。
参考gin
https://gist.github.com/mattetti/5914158
以上是内存溢出为你收集整理的golang 上传文件(包括 gin 实现)全部内容,希望文章能够帮你解决golang 上传文件(包括 gin 实现)所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)