Flutter发送Multipart数据到Golang接收解析及文件存S3方案咨询
实现说明
你遇到的核心问题是Flutter发送的是multipart/form-data格式请求,你定义的结构体使用的json标签无法直接解析该类请求,且原结构体定义不符合参数结构(pages是数组类型而非单个结构体)。以下是完整可运行的示例代码:
完整代码实现
package main import ( "context" "encoding/json" "fmt" "mime/multipart" "net/http" "strconv" "strings" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" ) // 调整后的参数接收结构体 type Page struct { Order uint `json:"order"` Description string `json:"description"` Images []*multipart.FileHeader `json:"images"` // 存储文件头信息而非直接存文件对象 } type Tag struct { TagId uint `json:"id"` TagName string `json:"tag_name"` } type ReceiveData struct { Title string `json:"title"` Date string `json:"date"` TagList []Tag `json:"tag_list"` Pages []Page `json:"pages"` } // 全局S3客户端 var s3Client *s3.Client var bucketName = "替换为你的S3桶名" // 初始化S3客户端 func initS3() { cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("替换为你的S3区域,比如ap-beijing")) if err != nil { panic(fmt.Sprintf("S3客户端初始化失败: %v", err)) } s3Client = s3.NewFromConfig(cfg) } // 上传文件到S3工具方法 func uploadToS3(file *multipart.FileHeader, s3FilePath string) error { src, err := file.Open() if err != nil { return err } defer src.Close() _, err = s3Client.PutObject(context.TODO(), &s3.PutObjectInput{ Bucket: aws.String(bucketName), Key: aws.String(s3FilePath), Body: src, }) return err } // 请求处理接口 func testHandler(w http.ResponseWriter, r *http.Request) { // 1. 解析multipart表单,设置最大内存为100M,超出部分会自动写入临时磁盘文件 err := r.ParseMultipartForm(100 << 20) if err != nil { http.Error(w, fmt.Sprintf("表单解析失败: %v", err), http.StatusBadRequest) return } var reqData ReceiveData // 2. 读取基础字段 reqData.Title = r.FormValue("title") reqData.Date = r.FormValue("date") // 解析tag_list:Flutter端传的是序列化后的JSON字符串,直接反序列化即可 tagStr := r.FormValue("tag_list") if tagStr != "" { err = json.Unmarshal([]byte(tagStr), &reqData.TagList) if err != nil { http.Error(w, fmt.Sprintf("tag_list解析失败: %v", err), http.StatusBadRequest) return } } // 3. 解析pages数组的文本字段 pageMap := make(map[int]*Page) for key := range r.PostForm { if !strings.HasPrefix(key, "pages[") { continue } var idx int var field string _, err = fmt.Sscanf(key, "pages[%d][%s]", &idx, &field) if err != nil { continue } if _, ok := pageMap[idx]; !ok { pageMap[idx] = &Page{} } // Flutter端用了jsonEncode转义字段,需要先去掉外层引号 val := strings.Trim(r.PostForm.Get(key), `"`) switch field { case "order": order, _ := strconv.Atoi(val) pageMap[idx].Order = uint(order) case "description": pageMap[idx].Description = val } } // 4. 解析pages数组对应的图片文件 for key, files := range r.MultipartForm.File { if !strings.HasPrefix(key, "pages[") { continue } var idx int var field string _, err = fmt.Sscanf(key, "pages[%d][%s]", &idx, &field) if err != nil || field != "images" { continue } if _, ok := pageMap[idx]; !ok { pageMap[idx] = &Page{} } pageMap[idx].Images = files } // 把无序的pageMap转为有序数组 reqData.Pages = make([]Page, len(pageMap)) for i := 0; i < len(pageMap); i++ { reqData.Pages[i] = *pageMap[i] } // 5. 批量上传图片到S3 for pageIdx, page := range reqData.Pages { for imgIdx, imgFile := range page.Images { // 自定义S3存储路径,可根据业务调整 savePath := fmt.Sprintf("diary/%s_page%d_img%d_%s", reqData.Date, pageIdx, imgIdx, imgFile.Filename) err = uploadToS3(imgFile, savePath) if err != nil { http.Error(w, fmt.Sprintf("图片上传失败: %v", err), http.StatusInternalServerError) return } // 这里可自行添加逻辑,把S3路径存储到数据库 } } // 后续业务逻辑处理,比如把reqData写入数据库等 w.WriteHeader(http.StatusOK) w.Write([]byte("处理成功")) } func main() { initS3() http.HandleFunc("/test", testHandler) fmt.Println("服务启动在: 0.0.0.0:8080") http.ListenAndServe(":8080", nil) }
注意事项
- 需要先安装依赖:执行
go get github.com/aws/aws-sdk-go-v2 github.com/aws/aws-sdk-go-v2/service/s3 - 代码中的S3桶名、区域需要替换为你自己的配置,如果使用阿里云OSS、腾讯云COS等S3兼容服务,需要在S3初始化时额外配置Endpoint
- 可以根据业务需求调整表单最大内存限制,当前设置为100M
- 原结构体中的Location、Address字段Flutter端没有传递,需要的话可以在Flutter端添加到request.fields中,Go端用
r.FormValue("字段名")即可读取
内容的提问来源于stack exchange,提问作者Choi yun seok
相关产品推荐
相关产品推荐

