猿问

Echo web 框架绑定 FormFile

我正在将 Echo 框架用于接受表单数据的 post 端点。我使用 Struct 作为绑定模型来提取表单数据。我的绑定模型和上传处理程序代码如下所示。


 type FormModel struct {

    ID string                `form:"ID"`

    FirstName string                `form:"FirstName"`

    File      *multipart.FileHeader `form:"myFileName"`

}


func (cs *handler) uploadForm(c echo.Context) error {

s := new(FormModel)

if err := c.Bind(s); err != nil {

    return nil

}


fileHandler, err := c.FormFile("myFileName")

我可以通过绑定获得 ID 和 FirstName 等表单值。但我无法在绑定期间获取文件。我必须使用fileHandler, err := c.FormFile("myFileName")来获取文件。有没有办法在绑定模型中获取文件信息?


慕田峪7331174
浏览 251回答 1
1回答

幕布斯7119047

echo 默认不支持绑定 multipart.Form.File 数据,需要重新绑定实现接口。为 echo Bind 封装了一层额外的 bind FormFile。如果结构指针类型的属性类型为*multipart.FileHeader 或[]*multipart.FileHeader,则该属性将通过反射设置为FormFile 的值。我大概实现了这个功能。我没有使用过echo,也没有测试过,但想法是正确的。最后更新:添加示例并修复代码以构建。感谢@vicTROLLA 指出typeMultipartFileHeader类型定义错误。package mainimport (&nbsp; &nbsp; "bytes"&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "github.com/labstack/echo"&nbsp; &nbsp; "io/ioutil"&nbsp; &nbsp; "mime/multipart"&nbsp; &nbsp; "net/http"&nbsp; &nbsp; "reflect"&nbsp; &nbsp; "strings"&nbsp; &nbsp; "time")var (&nbsp; &nbsp; typeMultipartFileHeader&nbsp; &nbsp; &nbsp; = reflect.TypeOf((*multipart.FileHeader)(nil))&nbsp; &nbsp; typeMultipartSliceFileHeader = reflect.TypeOf(([]*multipart.FileHeader)(nil)))type File struct {&nbsp; &nbsp; File&nbsp; *multipart.FileHeader&nbsp; &nbsp;`form:"file"`&nbsp; &nbsp; Files []*multipart.FileHeader `form:"files"`}func main() {&nbsp; &nbsp; app := echo.New()&nbsp; &nbsp; // warp bind suppet bind FormFile&nbsp; &nbsp; app.Binder = NewBindFile(app.Binder)&nbsp; &nbsp; app.Any("/file", func(ctx echo.Context) error {&nbsp; &nbsp; &nbsp; &nbsp; var file File&nbsp; &nbsp; &nbsp; &nbsp; err := ctx.Bind(&file)&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; readfile := func(file *multipart.FileHeader) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f, err := file.Open()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("open %s error: %s\n", file.Filename, err.Error())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; body, err := ioutil.ReadAll(f)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("read file %s error: %v body: %s\n", file.Filename, err, body)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; readfile(file.File)&nbsp; &nbsp; &nbsp; &nbsp; for _, file := range file.Files {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; readfile(file)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; })&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(200 * time.Millisecond)&nbsp; &nbsp; &nbsp; &nbsp; buf := bytes.NewBuffer(nil)&nbsp; &nbsp; &nbsp; &nbsp; w := multipart.NewWriter(buf)&nbsp; &nbsp; &nbsp; &nbsp; part, _ := w.CreateFormFile("file", "file")&nbsp; &nbsp; &nbsp; &nbsp; part.Write([]byte("this one file"))&nbsp; &nbsp; &nbsp; &nbsp; part, _ = w.CreateFormFile("files", "files")&nbsp; &nbsp; &nbsp; &nbsp; part.Write([]byte("fils part 1"))&nbsp; &nbsp; &nbsp; &nbsp; part, _ = w.CreateFormFile("files", "files")&nbsp; &nbsp; &nbsp; &nbsp; part.Write([]byte("fils part 2"))&nbsp; &nbsp; &nbsp; &nbsp; part, _ = w.CreateFormFile("files", "files")&nbsp; &nbsp; &nbsp; &nbsp; part.Write([]byte("fils part 3"))&nbsp; &nbsp; &nbsp; &nbsp; part, _ = w.CreateFormFile("files", "files")&nbsp; &nbsp; &nbsp; &nbsp; part.Write([]byte("fils part 4"))&nbsp; &nbsp; &nbsp; &nbsp; w.Close()&nbsp; &nbsp; &nbsp; &nbsp; http.Post("http://localhost:1323/file", w.FormDataContentType(), buf)&nbsp; &nbsp; }()&nbsp; &nbsp; app.Start(":1323")}type BindFunc func(interface{}, echo.Context) errorfunc (fn BindFunc) Bind(i interface{}, ctx echo.Context) error {&nbsp; &nbsp; return fn(i, ctx)}func NewBindFile(b echo.Binder) echo.Binder {&nbsp; &nbsp; return BindFunc(func(i interface{}, ctx echo.Context) error {&nbsp; &nbsp; &nbsp; &nbsp; err := b.Bind(i, ctx)&nbsp; &nbsp; &nbsp; &nbsp; if err == nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ctype := ctx.Request().Header.Get(echo.HeaderContentType)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // if bind form&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if strings.HasPrefix(ctype, echo.MIMEApplicationForm) || strings.HasPrefix(ctype, echo.MIMEMultipartForm) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // get form files&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var form *multipart.Form&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; form, err = ctx.MultipartForm()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err == nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err = EchoBindFile(i, ctx, form.File)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; })}func EchoBindFile(i interface{}, ctx echo.Context, files map[string][]*multipart.FileHeader) error {&nbsp; &nbsp; iValue := reflect.Indirect(reflect.ValueOf(i))&nbsp; &nbsp; // check bind type is struct pointer&nbsp; &nbsp; if iValue.Kind() != reflect.Struct {&nbsp; &nbsp; &nbsp; &nbsp; return fmt.Errorf("BindFile input not is struct pointer, indirect type is %s", iValue.Type().String())&nbsp; &nbsp; }&nbsp; &nbsp; iType := iValue.Type()&nbsp; &nbsp; for i := 0; i < iType.NumField(); i++ {&nbsp; &nbsp; &nbsp; &nbsp; fType := iType.Field(i)&nbsp; &nbsp; &nbsp; &nbsp; // check canset field&nbsp; &nbsp; &nbsp; &nbsp; fValue := iValue.Field(i)&nbsp; &nbsp; &nbsp; &nbsp; if !fValue.CanSet() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; // revc type must *multipart.FileHeader or []*multipart.FileHeader&nbsp; &nbsp; &nbsp; &nbsp; switch fType.Type {&nbsp; &nbsp; &nbsp; &nbsp; case typeMultipartFileHeader:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; file := getFiles(files, fType.Name, fType.Tag.Get("form"))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if len(file) > 0 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fValue.Set(reflect.ValueOf(file[0]))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; case typeMultipartSliceFileHeader:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; file := getFiles(files, fType.Name, fType.Tag.Get("form"))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if len(file) > 0 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fValue.Set(reflect.ValueOf(file))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; return nil}func getFiles(files map[string][]*multipart.FileHeader, names ...string) []*multipart.FileHeader {&nbsp; &nbsp; for _, name := range names {&nbsp; &nbsp; &nbsp; &nbsp; file, ok := files[name]&nbsp; &nbsp; &nbsp; &nbsp; if ok {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return file&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; return nil}
随时随地看视频慕课网APP

相关分类

Go
我要回答