improve uploading file feature for ghttp.Server; improve package gfile/gstr/gdebug

This commit is contained in:
John
2020-03-07 19:31:33 +08:00
parent 7f0163d958
commit a34ca0ff4b
8 changed files with 235 additions and 44 deletions

View File

@ -9,6 +9,7 @@ package ghttp
import (
"bytes"
"encoding/json"
"fmt"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/encoding/gjson"
"github.com/gogf/gf/encoding/gurl"
@ -302,5 +303,19 @@ func (r *Request) GetMultipartFiles(name string) []*multipart.FileHeader {
if v := form.File[name+"[]"]; len(v) > 0 {
return v
}
// Support "name[0]","name[1]","name[2]", etc. as array parameter.
key := ""
files := make([]*multipart.FileHeader, 0)
for i := 0; ; i++ {
key = fmt.Sprintf(`%s[%d]`, name, i)
if v := form.File[key]; len(v) > 0 {
files = append(files, v[0])
} else {
break
}
}
if len(files) > 0 {
return files
}
return nil
}

View File

@ -26,7 +26,7 @@ type UploadFile struct {
// UploadFiles is array type for *UploadFile.
type UploadFiles []*UploadFile
// Save saves the single uploading file to specified path.
// Save saves the single uploading file to specified path and returns the saved file name.
// The parameter path can be either a directory or a file path. If <path> is a directory,
// it saves the uploading file to the directory using its original name. If <path> is a
// file path, it saves the uploading file to the file path.
@ -35,56 +35,61 @@ type UploadFiles []*UploadFile
// make sense if the <path> is a directory.
//
// Note that it will overwrite the target file if there's already a same name file exist.
func (f *UploadFile) Save(path string, randomlyRename ...bool) error {
func (f *UploadFile) Save(path string, randomlyRename ...bool) (filename string, err error) {
if f == nil {
return nil
return
}
file, err := f.Open()
if err != nil {
return err
return "", err
}
defer file.Close()
filePath := path
if gfile.IsDir(path) {
filename := gfile.Basename(f.Filename)
name := gfile.Basename(f.Filename)
if len(randomlyRename) > 0 && randomlyRename[0] {
filename = strings.ToLower(strconv.FormatInt(gtime.TimestampNano(), 36) + grand.S(6))
filename = filename + gfile.Ext(f.Filename)
name = strings.ToLower(strconv.FormatInt(gtime.TimestampNano(), 36) + grand.S(6))
name = name + gfile.Ext(f.Filename)
}
filePath = gfile.Join(path, filename)
filePath = gfile.Join(path, name)
}
newFile, err := gfile.Create(filePath)
if err != nil {
return err
return "", err
}
defer newFile.Close()
intlog.Printf(`save upload file: %s`, filePath)
if _, err := io.Copy(newFile, file); err != nil {
return err
return "", err
}
return nil
return gfile.Basename(filePath), nil
}
// Save saves all uploading files to specified directory path.
// Save saves all uploading files to specified directory path and returns the saved file names.
//
// The parameter <dirPath> should be a directory path or it returns error.
//
// The parameter <randomlyRename> specifies whether randomly renames all the file names.
func (fs UploadFiles) Save(dirPath string, randomlyRename ...bool) error {
func (fs UploadFiles) Save(dirPath string, randomlyRename ...bool) (filenames []string, err error) {
if len(fs) == 0 {
return nil
return nil, nil
}
if !gfile.IsDir(dirPath) {
return errors.New(`parameter "dirPath" should be a directory path`)
if !gfile.Exists(dirPath) {
if err = gfile.Mkdir(dirPath); err != nil {
return
}
} else if !gfile.IsDir(dirPath) {
return nil, errors.New(`parameter "dirPath" should be a directory path`)
}
var err error
for _, f := range fs {
if err = f.Save(dirPath, randomlyRename...); err != nil {
return err
if filename, err := f.Save(dirPath, randomlyRename...); err != nil {
return filenames, err
} else {
filenames = append(filenames, filename)
}
}
return nil
return
}
// GetUploadFile retrieves and returns the uploading file with specified form name.

View File

@ -0,0 +1,146 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package ghttp_test
import (
"fmt"
"github.com/gogf/gf/debug/gdebug"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/text/gstr"
"testing"
"time"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/test/gtest"
)
func Test_Params_File_Single(t *testing.T) {
dstDirPath := gfile.Join(gfile.TempDir(), gtime.TimestampNanoStr())
err := gfile.Mkdir(dstDirPath)
gtest.Assert(err, nil)
p := ports.PopRand()
s := g.Server(p)
s.BindHandler("/upload/single", func(r *ghttp.Request) {
file := r.GetUploadFile("file")
if file == nil {
r.Response.WriteExit("upload file cannot be empty")
}
if name, err := file.Save(dstDirPath, r.GetBool("randomlyRename")); err == nil {
r.Response.WriteExit(name)
}
r.Response.WriteExit("upload failed")
})
s.SetPort(p)
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()
time.Sleep(100 * time.Millisecond)
// normal name
gtest.Case(t, func() {
client := ghttp.NewClient()
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
srcPath := gfile.Join(gdebug.CallerDirectory(), "testdata", "upload", "file1.txt")
dstPath := gfile.Join(dstDirPath, "file1.txt")
content := client.PostContent("/upload/single", g.Map{
"file": "@file:" + srcPath,
})
gtest.AssertNE(content, "")
gtest.AssertNE(content, "upload file cannot be empty")
gtest.AssertNE(content, "upload failed")
gtest.Assert(content, "file1.txt")
gtest.Assert(gfile.GetContents(dstPath), gfile.GetContents(srcPath))
})
// randomly rename.
gtest.Case(t, func() {
client := ghttp.NewClient()
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
srcPath := gfile.Join(gdebug.CallerDirectory(), "testdata", "upload", "file2.txt")
content := client.PostContent("/upload/single", g.Map{
"file": "@file:" + srcPath,
"randomlyRename": true,
})
dstPath := gfile.Join(dstDirPath, content)
gtest.AssertNE(content, "")
gtest.AssertNE(content, "upload file cannot be empty")
gtest.AssertNE(content, "upload failed")
gtest.Assert(gfile.GetContents(dstPath), gfile.GetContents(srcPath))
})
}
func Test_Params_File_Batch(t *testing.T) {
dstDirPath := gfile.Join(gfile.TempDir(), gtime.TimestampNanoStr())
err := gfile.Mkdir(dstDirPath)
gtest.Assert(err, nil)
p := ports.PopRand()
s := g.Server(p)
s.BindHandler("/upload/batch", func(r *ghttp.Request) {
files := r.GetUploadFiles("file")
if files == nil {
r.Response.WriteExit("upload file cannot be empty")
}
if names, err := files.Save(dstDirPath, r.GetBool("randomlyRename")); err == nil {
r.Response.WriteExit(gstr.Join(names, ","))
}
r.Response.WriteExit("upload failed")
})
s.SetPort(p)
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()
time.Sleep(100 * time.Millisecond)
// normal name
gtest.Case(t, func() {
client := ghttp.NewClient()
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
srcPath1 := gfile.Join(gdebug.CallerDirectory(), "testdata", "upload", "file1.txt")
srcPath2 := gfile.Join(gdebug.CallerDirectory(), "testdata", "upload", "file2.txt")
dstPath1 := gfile.Join(dstDirPath, "file1.txt")
dstPath2 := gfile.Join(dstDirPath, "file2.txt")
content := client.PostContent("/upload/batch", g.Map{
"file[0]": "@file:" + srcPath1,
"file[1]": "@file:" + srcPath2,
})
gtest.AssertNE(content, "")
gtest.AssertNE(content, "upload file cannot be empty")
gtest.AssertNE(content, "upload failed")
gtest.Assert(content, "file1.txt,file2.txt")
gtest.Assert(gfile.GetContents(dstPath1), gfile.GetContents(srcPath1))
gtest.Assert(gfile.GetContents(dstPath2), gfile.GetContents(srcPath2))
})
// randomly rename.
gtest.Case(t, func() {
client := ghttp.NewClient()
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
srcPath1 := gfile.Join(gdebug.CallerDirectory(), "testdata", "upload", "file1.txt")
srcPath2 := gfile.Join(gdebug.CallerDirectory(), "testdata", "upload", "file2.txt")
content := client.PostContent("/upload/batch", g.Map{
"file[0]": "@file:" + srcPath1,
"file[1]": "@file:" + srcPath2,
"randomlyRename": true,
})
gtest.AssertNE(content, "")
gtest.AssertNE(content, "upload file cannot be empty")
gtest.AssertNE(content, "upload failed")
array := gstr.SplitAndTrim(content, ",")
gtest.Assert(len(array), 2)
dstPath1 := gfile.Join(dstDirPath, array[0])
dstPath2 := gfile.Join(dstDirPath, array[1])
gtest.Assert(gfile.GetContents(dstPath1), gfile.GetContents(srcPath1))
gtest.Assert(gfile.GetContents(dstPath2), gfile.GetContents(srcPath2))
})
}

1
net/ghttp/testdata/upload/file1.txt vendored Normal file
View File

@ -0,0 +1 @@
file1.txt: This file is for uploading unit test case.

1
net/ghttp/testdata/upload/file2.txt vendored Normal file
View File

@ -0,0 +1 @@
file2.txt: This file is for uploading unit test case.