GoFrame 支持非常方便的表单文件上传功能,并且HTTP客户端对上传功能进行了必要的封装并极大简化了上传功能调用。

HTTPClient-文件上传 - 图1注意

注意哦:上传文件大小受到 ghttp.ServerClientMaxBodySize 配置影响: https://pkg.go.dev/github.com/gogf/gf/v2/net/ghttp#ServerConfig 默认支持的上传文件大小为 8MB

服务端

在服务端通过 Request 对象获取上传文件:

  1. package main
  2. import (
  3. "github.com/gogf/gf/v2/frame/g"
  4. "github.com/gogf/gf/v2/net/ghttp"
  5. )
  6. // Upload uploads files to /tmp .
  7. func Upload(r *ghttp.Request) {
  8. files := r.GetUploadFiles("upload-file")
  9. names, err := files.Save("/tmp/")
  10. if err != nil {
  11. r.Response.WriteExit(err)
  12. }
  13. r.Response.WriteExit("upload successfully: ", names)
  14. }
  15. // UploadShow shows uploading simgle file page.
  16. func UploadShow(r *ghttp.Request) {
  17. r.Response.Write(`
  18. <html>
  19. <head>
  20. <title>GoFrame Upload File Demo</title>
  21. </head>
  22. <body>
  23. <form enctype="multipart/form-data" action="/upload" method="post">
  24. <input type="file" name="upload-file" />
  25. <input type="submit" value="upload" />
  26. </form>
  27. </body>
  28. </html>
  29. `)
  30. }
  31. // UploadShowBatch shows uploading multiple files page.
  32. func UploadShowBatch(r *ghttp.Request) {
  33. r.Response.Write(`
  34. <html>
  35. <head>
  36. <title>GoFrame Upload Files Demo</title>
  37. </head>
  38. <body>
  39. <form enctype="multipart/form-data" action="/upload" method="post">
  40. <input type="file" name="upload-file" />
  41. <input type="file" name="upload-file" />
  42. <input type="submit" value="upload" />
  43. </form>
  44. </body>
  45. </html>
  46. `)
  47. }
  48. func main() {
  49. s := g.Server()
  50. s.Group("/upload", func(group *ghttp.RouterGroup) {
  51. group.POST("/", Upload)
  52. group.ALL("/show", UploadShow)
  53. group.ALL("/batch", UploadShowBatch)
  54. })
  55. s.SetPort(8199)
  56. s.Run()
  57. }

该服务端提供了3个接口:

  1. http://127.0.0.1:8199/upload/show 地址用于展示单个文件上传的H5页面;
  2. http://127.0.0.1:8199/upload/batch 地址用于展示多个文件上传的H5页面;
  3. http://127.0.0.1:8199/upload 接口用于真实的表单文件上传,该接口同时支持单个文件或者多个文件上传;

我们这里访问 http://127.0.0.1:8199/upload/show 选择需要上传的单个文件,提交之后可以看到文件上传成功到服务器上。

关键代码说明

  1. 我们在服务端可以通过 r.GetUploadFiles 方法获得上传的所有文件对象,也可以通过 r.GetUploadFile 获取单个上传的文件对象。
  2. r.GetUploadFiles("upload-file") 中的参数 "upload-file" 为本示例中客户端上传时的表单文件域名称,开发者可以根据前后端约定在客户端中定义,以方便服务端接收表单文件域参数。
  3. 通过 files.Save 可以将上传的多个文件方便地保存到指定的目录下,并返回保存成功的文件名。如果是批量保存,只要任意一个文件保存失败,都将会立即返回错误。此外, Save 方法的第二个参数支持随机自动命名上传文件。
  4. 通过 group.POST("/", Upload) 注册的路由仅支持 POST 方式访问。

客户端

单文件上传

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/v2/frame/g"
  5. "github.com/gogf/gf/v2/os/gctx"
  6. "github.com/gogf/gf/v2/os/glog"
  7. )
  8. func main() {
  9. var (
  10. ctx = gctx.New()
  11. path = "/home/john/Workspace/Go/github.com/gogf/gf/v2/version.go"
  12. )
  13. result, err := g.Client().Post(ctx, "http://127.0.0.1:8199/upload", "upload-file=@file:"+path)
  14. if err != nil {
  15. glog.Fatalf(ctx, `%+v`, err)
  16. }
  17. defer result.Close()
  18. fmt.Println(result.ReadAllString())
  19. }

注意到了吗?文件上传参数格式使用了 参数名=@file:文件路径 ,HTTP客户端将会自动解析 文件路径 对应的文件内容并读取提交给服务端。原本复杂的文件上传操作被 gf 进行了封装处理,用户只需要使用 @file:+文件路径 来构成参数值即可。其中, 文件路径 请使用本地文件绝对路径。

首先运行服务端程序之后,我们再运行这个上传客户端(注意修改上传的文件路径为本地真实文件路径),执行后可以看到文件被成功上传到服务器的指定路径下。

多文件上传

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/v2/frame/g"
  5. "github.com/gogf/gf/v2/os/gctx"
  6. "github.com/gogf/gf/v2/os/glog"
  7. )
  8. func main() {
  9. var (
  10. ctx = gctx.New()
  11. path1 = "/Users/john/Pictures/logo1.png"
  12. path2 = "/Users/john/Pictures/logo2.png"
  13. )
  14. result, err := g.Client().Post(
  15. ctx,
  16. "http://127.0.0.1:8199/upload",
  17. fmt.Sprintf(`upload-file=@file:%s&upload-file=@file:%s`, path1, path2),
  18. )
  19. if err != nil {
  20. glog.Fatalf(ctx, `%+v`, err)
  21. }
  22. defer result.Close()
  23. fmt.Println(result.ReadAllString())
  24. }

可以看到,多个文件上传提交参数格式为 参数名=@file:xxx&参数名=@file:xxx...,也可以使用 参数名[]=@file:xxx&参数名[]=@file:xxx... 的形式。

首先运行服务端程序之后,我们再运行这个上传客户端(注意修改上传的文件路径为本地真实文件路径),执行后可以看到文件被成功上传到服务器的指定路径下。

自定义文件名称

很简单,修改 FileName 属性即可。

  1. s := g.Server()
  2. s.BindHandler("/upload", func(r *ghttp.Request) {
  3. file := r.GetUploadFile("TestFile")
  4. if file == nil {
  5. r.Response.Write("empty file")
  6. return
  7. }
  8. file.Filename = "MyCustomFileName.txt"
  9. fileName, err := file.Save(gfile.TempDir())
  10. if err != nil {
  11. r.Response.Write(err)
  12. return
  13. }
  14. r.Response.Write(fileName)
  15. })
  16. s.SetPort(8999)
  17. s.Run()

规范路由接收上传文件

服务端如果通过规范路由方式,那么可以通过结构化的参数获取上传文件:

  • 参数接收的数据类型使用 *ghttp.UploadFile
  • 如果需要接口文档也支持文件类型,那么参数的标签中设置 typefile 类型

HTTPClient-文件上传 - 图2

HTTPClient-文件上传 - 图3