Skip to content

使用go-zero上传文件,其实用的还是net/http包里面的http.request对象,在默认生成的代码中,go-zero会将http.request解析成对应的request,但是当在上传文件时,不能这样做,需要换一个处理方法,下面就来演示一下,其实这些代码就是https://github.com/zeromicro/zero-examples/tree/main/monolithic的内容

api文件

syntax = "v1"

type UploadResponse {
	Code int `json:"code"`
}

service file-api {
	@handler UploadHandler
	post /upload returns (UploadResponse)
}

handler的处理

go
package handler
func UploadHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
        // 这里由于不是普通的传递参数,requestBody中包含文件,所以将处理逻辑放到logic中
		l := logic.NewUploadLogic(r, svcCtx)
		resp, err := l.Upload()
		if err != nil {
			httpx.Error(w, err)
		} else {
			httpx.OkJson(w, resp)
		}
	}
}

logic

需要上传相关logic的结构体的组成,需要传入*http.Request

go
package logic
const maxFileSize = 10 << 20 // 10 MB

type UploadLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
	r      *http.Request
}

func NewUploadLogic(r *http.Request, svcCtx *svc.ServiceContext) UploadLogic {
	return UploadLogic{
		Logger: logx.WithContext(r.Context()),
		r:      r,
		svcCtx: svcCtx,
	}
}

func (l *UploadLogic) Upload() (resp *types.UploadResponse, err error) {
	err = l.r.ParseMultipartForm(maxFileSize)
	if err != nil {
		return nil, err
	}
    // 获取key为myFile的文件
	file, handler, err := l.r.FormFile("myFile")

	if err != nil {
		return nil, err
	}
	defer file.Close()

	logx.Infof("upload file: %+v, file size: %d, MIME header: %+v",
		handler.Filename, handler.Size, handler.Header)

	tempFile, err := os.Create(path.Join(l.svcCtx.Config.Path, handler.Filename))
	if err != nil {
		return nil, err
	}
	defer tempFile.Close()
	io.Copy(tempFile, file)

	return &types.UploadResponse{
		Code: 0,
	}, nil
}

需要注意这里,在获取其中的文件之前需要先调用ParseMultipartForm,不然无法获取文件

多文件上传

这里没有采用使用http.Request的FormFile方法,直接将MultipartForm.File拿出来,对其进行处理,需要注意的是它的类型是map[string][]*FileHeader,这个map的键是,form表单的key,由于form的key对应着多个文件,所以这里的类型是[]*FileHeader,所以说如果要考虑到这种情况的话,需要双循环

go
package logic

const maxFileSize = 1 << 20 // 1000 MB

type UploadFilesLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
	r      *http.Request
}

func NewUploadFilesLogic(ctx context.Context, svcCtx *svc.ServiceContext, r *http.Request) *UploadFilesLogic {
	return &UploadFilesLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
		r:      r,
	}
}

func (l *UploadFilesLogic) UploadFiles() (resp *types.UploadResponse, err error) {
	err = l.r.ParseMultipartForm(maxFileSize)
	if err != nil {
		return nil, err
	}
	for _, fileHeaders := range l.r.MultipartForm.File {
		for _, header := range fileHeaders {
			filename := header.Filename
			open, err := header.Open()
			defer open.Close()
			if err != nil {
				return nil, err
			}
			fp, err := os.Create(filename)
			defer fp.Close()
			if err != nil {
				return nil, err
			}
			_, err = io.Copy(fp, open)
		}

	}
	return &types.UploadResponse{Code: 200}, nil
}