improve file uploading using strict route feature

This commit is contained in:
John Guo
2022-03-02 21:15:16 +08:00
parent d64898c59a
commit 4e2d378145
6 changed files with 75 additions and 11 deletions

View File

@ -23,8 +23,8 @@ import (
// UploadFile wraps the multipart uploading file with more and convenient features.
type UploadFile struct {
*multipart.FileHeader
ctx context.Context
*multipart.FileHeader `json:"-"`
ctx context.Context
}
// UploadFiles is array type for *UploadFile.
@ -32,7 +32,7 @@ type UploadFiles []*UploadFile
// Save saves the single uploading file to directory path and returns the saved file name.
//
// The parameter `dirPath` should be a directory path or it returns error.
// The parameter `dirPath` should be a directory path, or it returns error.
//
// Note that it will OVERWRITE the target file if there's already a same name file exist.
func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename string, err error) {

View File

@ -66,14 +66,10 @@ func (r *Request) GetRequestMap(kvMap ...map[string]interface{}) map[string]inte
var (
ok, filter bool
)
var length int
if len(kvMap) > 0 && kvMap[0] != nil {
length = len(kvMap[0])
filter = true
} else {
length = len(r.routerMap) + len(r.queryMap) + len(r.formMap) + len(r.bodyMap) + len(r.paramsMap)
}
m := make(map[string]interface{}, length)
m := make(map[string]interface{})
for k, v := range r.routerMap {
if filter {
if _, ok = kvMap[0][k]; !ok {
@ -114,6 +110,16 @@ func (r *Request) GetRequestMap(kvMap ...map[string]interface{}) map[string]inte
}
m[k] = v
}
// File uploading.
if r.MultipartForm != nil {
for name := range r.MultipartForm.File {
if uploadFiles := r.GetUploadFiles(name); len(uploadFiles) == 1 {
m[name] = uploadFiles[0]
} else {
m[name] = uploadFiles
}
}
}
// Check none exist parameters and assign it with default value.
if filter {
for k, v := range kvMap[0] {
@ -171,9 +177,11 @@ func (r *Request) doGetRequestStruct(pointer interface{}, mapping ...map[string]
if data == nil {
data = map[string]interface{}{}
}
// Default struct values.
if err = r.mergeDefaultStructValue(data, pointer); err != nil {
return data, nil
}
return data, gconv.Struct(data, pointer, mapping...)
}

View File

@ -7,6 +7,7 @@
package ghttp_test
import (
"context"
"fmt"
"testing"
"time"
@ -18,6 +19,7 @@ import (
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gmeta"
"github.com/gogf/gf/v2/util/guid"
)
@ -169,3 +171,49 @@ func Test_Params_File_Batch(t *testing.T) {
t.Assert(gfile.GetContents(dstPath2), gfile.GetContents(srcPath2))
})
}
func Test_Params_Strict_Route_File_Single(t *testing.T) {
type Req struct {
gmeta.Meta `method:"post" mime:"multipart/form-data"`
File *ghttp.UploadFile `type:"file"`
}
type Res struct{}
dstDirPath := gfile.Temp(gtime.TimestampNanoStr())
s := g.Server(guid.S())
s.BindHandler("/upload/single", func(ctx context.Context, req *Req) (res *Res, err error) {
var (
r = g.RequestFromCtx(ctx)
file = req.File
)
if file == nil {
r.Response.WriteExit("upload file cannot be empty")
}
name, err := file.Save(dstDirPath)
if err != nil {
r.Response.WriteExit(err)
}
r.Response.WriteExit(name)
return
})
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()
time.Sleep(100 * time.Millisecond)
// normal name
gtest.C(t, func(t *gtest.T) {
client := g.Client()
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
srcPath := gdebug.TestDataPath("upload", "file1.txt")
dstPath := gfile.Join(dstDirPath, "file1.txt")
content := client.PostContent(ctx, "/upload/single", g.Map{
"file": "@file:" + srcPath,
})
t.AssertNE(content, "")
t.AssertNE(content, "upload file cannot be empty")
t.AssertNE(content, "upload failed")
t.Assert(content, "file1.txt")
t.Assert(gfile.GetContents(dstPath), gfile.GetContents(srcPath))
})
}

View File

@ -82,6 +82,7 @@ const (
TagNamePath = `path`
TagNameMethod = `method`
TagNameMime = `mime`
TagNameConsumes = `consumes`
TagNameType = `type`
TagNameDomain = `domain`
TagNameValidate = `v`

View File

@ -54,7 +54,8 @@ func (oai *OpenApiV3) newParameterRefWithStructMethod(field gstructs.Field, path
parameter.Name = field.Name()
}
if len(tagMap) > 0 {
if err := gconv.Struct(oai.fileMapWithShortTags(tagMap), parameter); err != nil {
err := gconv.Struct(oai.fileMapWithShortTags(tagMap), parameter)
if err != nil {
return nil, gerror.Wrap(err, `mapping struct tags to Parameter failed`)
}
}

View File

@ -80,6 +80,7 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
}
var (
mime string
path = Path{}
inputMetaMap = gmeta.Data(inputObject.Interface())
outputMetaMap = gmeta.Data(outputObject.Interface())
@ -126,12 +127,17 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
}
if len(inputMetaMap) > 0 {
if err := gconv.Struct(oai.fileMapWithShortTags(inputMetaMap), &path); err != nil {
inputMetaMap = oai.fileMapWithShortTags(inputMetaMap)
if err := gconv.Struct(inputMetaMap, &path); err != nil {
return gerror.Wrap(err, `mapping struct tags to Path failed`)
}
if err := gconv.Struct(oai.fileMapWithShortTags(inputMetaMap), &operation); err != nil {
if err := gconv.Struct(inputMetaMap, &operation); err != nil {
return gerror.Wrap(err, `mapping struct tags to Operation failed`)
}
// Allowed request mime.
if mime = inputMetaMap[TagNameMime]; mime == "" {
mime = inputMetaMap[TagNameConsumes]
}
}
// =================================================================================================================