fix issue when only one file was uploaded in batch receiver attribute (#2365)

* fix fixed An error occurred when only one file was uploaded in batches and add unit testing(#2092)

* fix issue uploading files for ghttp.Server

Co-authored-by: yxh <yxh1103@qq.com>
This commit is contained in:
John Guo
2022-12-21 10:38:19 +08:00
committed by GitHub
parent e7b9e41a5e
commit 00c544ee99
2 changed files with 166 additions and 5 deletions

View File

@ -9,11 +9,12 @@ package ghttp_test
import (
"context"
"fmt"
"github.com/gogf/gf/v2/internal/json"
"strings"
"testing"
"time"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gfile"
@ -173,7 +174,7 @@ func Test_Params_File_Batch(t *testing.T) {
})
}
func Test_Params_Strict_Route_File_Single(t *testing.T) {
func Test_Params_Strict_Route_File_Single_Ptr_Attrr(t *testing.T) {
type Req struct {
gmeta.Meta `method:"post" mime:"multipart/form-data"`
File *ghttp.UploadFile `type:"file"`
@ -219,6 +220,48 @@ func Test_Params_Strict_Route_File_Single(t *testing.T) {
})
}
func Test_Params_Strict_Route_File_Single_Struct_Attr(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
)
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 := gtest.DataPath("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 failed")
t.Assert(content, "file1.txt")
t.Assert(gfile.GetContents(dstPath), gfile.GetContents(srcPath))
})
}
func Test_Params_File_Upload_Required(t *testing.T) {
type Req struct {
gmeta.Meta `method:"post" mime:"multipart/form-data"`
@ -274,3 +317,101 @@ func Test_Params_File_MarshalJSON(t *testing.T) {
t.Assert(strings.Contains(content, "file1.txt"), true)
})
}
// Select only one file when batch uploading
func Test_Params_Strict_Route_File_Batch_Up_One(t *testing.T) {
type Req struct {
gmeta.Meta `method:"post" mime:"multipart/form-data"`
Files ghttp.UploadFiles `type:"file"`
}
type Res struct{}
dstDirPath := gfile.Temp(gtime.TimestampNanoStr())
s := g.Server(guid.S())
s.BindHandler("/upload/batch", func(ctx context.Context, req *Req) (res *Res, err error) {
var (
r = g.RequestFromCtx(ctx)
files = req.Files
)
if len(files) == 0 {
r.Response.WriteExit("upload file cannot be empty")
}
names, err := files.Save(dstDirPath)
if err != nil {
r.Response.WriteExit(err)
}
r.Response.WriteExit(gstr.Join(names, ","))
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 := gtest.DataPath("upload", "file1.txt")
dstPath := gfile.Join(dstDirPath, "file1.txt")
content := client.PostContent(ctx, "/upload/batch", g.Map{
"files": "@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))
})
}
// Select multiple files during batch upload
func Test_Params_Strict_Route_File_Batch_Up_Multiple(t *testing.T) {
type Req struct {
gmeta.Meta `method:"post" mime:"multipart/form-data"`
Files ghttp.UploadFiles `type:"file"`
}
type Res struct{}
dstDirPath := gfile.Temp(gtime.TimestampNanoStr())
s := g.Server(guid.S())
s.BindHandler("/upload/batch", func(ctx context.Context, req *Req) (res *Res, err error) {
var (
r = g.RequestFromCtx(ctx)
files = req.Files
)
if len(files) == 0 {
r.Response.WriteExit("upload file cannot be empty")
}
names, err := files.Save(dstDirPath)
if err != nil {
r.Response.WriteExit(err)
}
r.Response.WriteExit(gstr.Join(names, ","))
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()))
srcPath1 := gtest.DataPath("upload", "file1.txt")
srcPath2 := gtest.DataPath("upload", "file2.txt")
dstPath1 := gfile.Join(dstDirPath, "file1.txt")
dstPath2 := gfile.Join(dstDirPath, "file2.txt")
content := client.PostContent(ctx, "/upload/batch",
"files=@file:"+srcPath1+
"&files=@file:"+srcPath2,
)
t.AssertNE(content, "")
t.AssertNE(content, "upload file cannot be empty")
t.AssertNE(content, "upload failed")
t.Assert(content, "file1.txt,file2.txt")
t.Assert(gfile.GetContents(dstPath1), gfile.GetContents(srcPath1))
t.Assert(gfile.GetContents(dstPath2), gfile.GetContents(srcPath2))
})
}

View File

@ -145,9 +145,29 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
// If `params` and `pointer` are the same type, the do directly assignment.
// For performance enhancement purpose.
if pointerElemReflectValue.IsValid() && pointerElemReflectValue.Type() == paramsReflectValue.Type() {
pointerElemReflectValue.Set(paramsReflectValue)
return nil
if pointerElemReflectValue.IsValid() {
switch {
// Eg:
// UploadFile => UploadFile
// *UploadFile => *UploadFile
case pointerElemReflectValue.Type() == paramsReflectValue.Type():
pointerElemReflectValue.Set(paramsReflectValue)
return nil
// Eg:
// UploadFile => *UploadFile
case pointerElemReflectValue.Kind() == reflect.Ptr && pointerElemReflectValue.Elem().IsValid() &&
pointerElemReflectValue.Elem().Type() == paramsReflectValue.Type():
pointerElemReflectValue.Elem().Set(paramsReflectValue)
return nil
// Eg:
// *UploadFile => UploadFile
case paramsReflectValue.Kind() == reflect.Ptr && paramsReflectValue.Elem().IsValid() &&
pointerElemReflectValue.Type() == paramsReflectValue.Elem().Type():
pointerElemReflectValue.Set(paramsReflectValue.Elem())
return nil
}
}
// Normal unmarshalling interfaces checks.