使用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
}