You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 21:15:02