Feat: {path}
marker in name rule representing the virtual path of the file
This commit is contained in:
parent
2e9f256462
commit
88a543ef74
8 changed files with 71 additions and 33 deletions
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
"github.com/HFO4/cloudreve/pkg/util"
|
||||
"github.com/jinzhu/gorm"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
@ -69,7 +70,7 @@ func (policy *Policy) SerializeOptions() (err error) {
|
|||
}
|
||||
|
||||
// GeneratePath 生成存储文件的路径
|
||||
func (policy *Policy) GeneratePath(uid uint) string {
|
||||
func (policy *Policy) GeneratePath(uid uint, path string) string {
|
||||
dirRule := policy.DirNameRule
|
||||
replaceTable := map[string]string{
|
||||
"{randomkey16}": util.RandStringRunes(16),
|
||||
|
@ -78,9 +79,10 @@ func (policy *Policy) GeneratePath(uid uint) string {
|
|||
"{uid}": strconv.Itoa(int(uid)),
|
||||
"{datetime}": time.Now().Format("20060102150405"),
|
||||
"{date}": time.Now().Format("20060102"),
|
||||
"{path}": path + "/",
|
||||
}
|
||||
dirRule = util.Replace(replaceTable, dirRule)
|
||||
return dirRule
|
||||
return filepath.Clean(dirRule)
|
||||
}
|
||||
|
||||
// GenerateFileName 生成存储文件名
|
||||
|
|
|
@ -46,25 +46,32 @@ func TestPolicy_GeneratePath(t *testing.T) {
|
|||
testPolicy := Policy{}
|
||||
|
||||
testPolicy.DirNameRule = "{randomkey16}"
|
||||
asserts.Len(testPolicy.GeneratePath(1), 16)
|
||||
asserts.Len(testPolicy.GeneratePath(1, "/"), 16)
|
||||
|
||||
testPolicy.DirNameRule = "{randomkey8}"
|
||||
asserts.Len(testPolicy.GeneratePath(1), 8)
|
||||
asserts.Len(testPolicy.GeneratePath(1, "/"), 8)
|
||||
|
||||
testPolicy.DirNameRule = "{timestamp}"
|
||||
asserts.Equal(testPolicy.GeneratePath(1), strconv.FormatInt(time.Now().Unix(), 10))
|
||||
asserts.Equal(testPolicy.GeneratePath(1, "/"), strconv.FormatInt(time.Now().Unix(), 10))
|
||||
|
||||
testPolicy.DirNameRule = "{uid}"
|
||||
asserts.Equal(testPolicy.GeneratePath(1), strconv.Itoa(int(1)))
|
||||
asserts.Equal(testPolicy.GeneratePath(1, "/"), strconv.Itoa(int(1)))
|
||||
|
||||
testPolicy.DirNameRule = "{datetime}"
|
||||
asserts.Len(testPolicy.GeneratePath(1), 14)
|
||||
asserts.Len(testPolicy.GeneratePath(1, "/"), 14)
|
||||
|
||||
testPolicy.DirNameRule = "{date}"
|
||||
asserts.Len(testPolicy.GeneratePath(1), 8)
|
||||
asserts.Len(testPolicy.GeneratePath(1, "/"), 8)
|
||||
|
||||
testPolicy.DirNameRule = "123{date}ss{datetime}"
|
||||
asserts.Len(testPolicy.GeneratePath(1), 27)
|
||||
asserts.Len(testPolicy.GeneratePath(1, "/"), 27)
|
||||
|
||||
testPolicy.DirNameRule = "/1/{path}/456"
|
||||
asserts.Condition(func() (success bool) {
|
||||
res := testPolicy.GeneratePath(1, "/23")
|
||||
return res == "/1/23/456" || res == "\\1\\23\\456"
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestPolicy_GenerateFileName(t *testing.T) {
|
||||
|
|
|
@ -10,13 +10,14 @@ import (
|
|||
"path/filepath"
|
||||
)
|
||||
|
||||
// FileData 上传来的文件数据处理器
|
||||
type FileData interface {
|
||||
// FileHeader 上传来的文件数据处理器
|
||||
type FileHeader interface {
|
||||
io.Reader
|
||||
io.Closer
|
||||
GetSize() uint64
|
||||
GetMIMEType() string
|
||||
GetFileName() string
|
||||
GetVirtualPath() string
|
||||
}
|
||||
|
||||
// Handler 存储策略适配器
|
||||
|
@ -77,7 +78,7 @@ func NewFileSystem(user *model.User) (*FileSystem, error) {
|
|||
*/
|
||||
|
||||
// Upload 上传文件
|
||||
func (fs *FileSystem) Upload(ctx context.Context, file FileData) (err error) {
|
||||
func (fs *FileSystem) Upload(ctx context.Context, file FileHeader) (err error) {
|
||||
ctx = context.WithValue(ctx, FileCtx, file)
|
||||
|
||||
// 上传前的钩子
|
||||
|
@ -89,7 +90,7 @@ func (fs *FileSystem) Upload(ctx context.Context, file FileData) (err error) {
|
|||
}
|
||||
|
||||
// 生成文件名和路径
|
||||
savePath := fs.GenerateSavePath(file)
|
||||
savePath := fs.GenerateSavePath(ctx, file)
|
||||
|
||||
// 处理客户端未完成上传时,关闭连接
|
||||
go fs.CancelUpload(ctx, savePath, file)
|
||||
|
@ -122,15 +123,21 @@ func (fs *FileSystem) Upload(ctx context.Context, file FileData) (err error) {
|
|||
}
|
||||
|
||||
// GenerateSavePath 生成要存放文件的路径
|
||||
func (fs *FileSystem) GenerateSavePath(file FileData) string {
|
||||
func (fs *FileSystem) GenerateSavePath(ctx context.Context, file FileHeader) string {
|
||||
return filepath.Join(
|
||||
fs.User.Policy.GeneratePath(fs.User.Model.ID),
|
||||
fs.User.Policy.GenerateFileName(fs.User.Model.ID, file.GetFileName()),
|
||||
fs.User.Policy.GeneratePath(
|
||||
fs.User.Model.ID,
|
||||
file.GetVirtualPath(),
|
||||
),
|
||||
fs.User.Policy.GenerateFileName(
|
||||
fs.User.Model.ID,
|
||||
file.GetFileName(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// CancelUpload 监测客户端取消上传
|
||||
func (fs *FileSystem) CancelUpload(ctx context.Context, path string, file FileData) {
|
||||
func (fs *FileSystem) CancelUpload(ctx context.Context, path string, file FileHeader) {
|
||||
ginCtx := ctx.Value(GinCtx).(*gin.Context)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
|
|
@ -4,12 +4,11 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"github.com/HFO4/cloudreve/pkg/util"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// GenericBeforeUpload 通用上传前处理钩子,包含数据库操作
|
||||
func GenericBeforeUpload(ctx context.Context, fs *FileSystem) error {
|
||||
file := ctx.Value(FileCtx).(FileData)
|
||||
file := ctx.Value(FileCtx).(FileHeader)
|
||||
|
||||
// 验证单文件尺寸
|
||||
if !fs.ValidateFileSize(ctx, file.GetSize()) {
|
||||
|
@ -35,7 +34,7 @@ func GenericBeforeUpload(ctx context.Context, fs *FileSystem) error {
|
|||
|
||||
// GenericAfterUploadCanceled 通用上传取消处理钩子,包含数据库操作
|
||||
func GenericAfterUploadCanceled(ctx context.Context, fs *FileSystem) error {
|
||||
file := ctx.Value(FileCtx).(FileData)
|
||||
file := ctx.Value(FileCtx).(FileHeader)
|
||||
|
||||
filePath := ctx.Value(SavePathCtx).(string)
|
||||
// 删除临时文件
|
||||
|
@ -55,10 +54,8 @@ func GenericAfterUploadCanceled(ctx context.Context, fs *FileSystem) error {
|
|||
|
||||
// GenericAfterUpload 文件上传完成后,包含数据库操作
|
||||
func GenericAfterUpload(ctx context.Context, fs *FileSystem) error {
|
||||
// 获取Gin的上下文
|
||||
ginCtx := ctx.Value(GinCtx).(*gin.Context)
|
||||
// 文件存放的虚拟路径
|
||||
virtualPath := util.DotPathToStandardPath(ginCtx.GetHeader("X-Path"))
|
||||
virtualPath := ctx.Value(FileCtx).(FileHeader).GetVirtualPath()
|
||||
|
||||
// 检查路径是否存在
|
||||
if !fs.IsPathExist(virtualPath) {
|
||||
|
|
|
@ -33,11 +33,16 @@ func (file FileData) GetFileName() string {
|
|||
return file.Name
|
||||
}
|
||||
|
||||
func (file FileData) GetVirtualPath() string {
|
||||
return file.Name
|
||||
}
|
||||
|
||||
type FileStream struct {
|
||||
File io.ReadCloser
|
||||
Size uint64
|
||||
Name string
|
||||
MIMEType string
|
||||
File io.ReadCloser
|
||||
Size uint64
|
||||
VirtualPath string
|
||||
Name string
|
||||
MIMEType string
|
||||
}
|
||||
|
||||
func (file FileStream) Read(p []byte) (n int, err error) {
|
||||
|
@ -59,3 +64,7 @@ func (file FileStream) Close() error {
|
|||
func (file FileStream) GetFileName() string {
|
||||
return file.Name
|
||||
}
|
||||
|
||||
func (file FileStream) GetVirtualPath() string {
|
||||
return file.VirtualPath
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
"path/filepath"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
}
|
||||
// Handler 本地策略适配器
|
||||
type Handler struct{}
|
||||
|
||||
// Put 将文件流保存到指定目录
|
||||
func (handler Handler) Put(ctx context.Context, file io.ReadCloser, dst string) error {
|
||||
|
|
14
pkg/util/path_test.go
Normal file
14
pkg/util/path_test.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDotPathToStandardPath(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
|
||||
asserts.Equal("/", DotPathToStandardPath(""))
|
||||
asserts.Equal("/目录", DotPathToStandardPath("目录"))
|
||||
asserts.Equal("/目录/目录2", DotPathToStandardPath("目录,目录2"))
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/HFO4/cloudreve/pkg/filesystem"
|
||||
"github.com/HFO4/cloudreve/pkg/filesystem/local"
|
||||
"github.com/HFO4/cloudreve/pkg/serializer"
|
||||
"github.com/HFO4/cloudreve/pkg/util"
|
||||
"github.com/HFO4/cloudreve/service/file"
|
||||
"github.com/gin-gonic/gin"
|
||||
"strconv"
|
||||
|
@ -54,10 +55,11 @@ func FileUploadStream(c *gin.Context) {
|
|||
}
|
||||
|
||||
fileData := local.FileStream{
|
||||
MIMEType: c.Request.Header.Get("Content-Type"),
|
||||
File: c.Request.Body,
|
||||
Size: fileSize,
|
||||
Name: c.Request.Header.Get("X-FileName"),
|
||||
MIMEType: c.Request.Header.Get("Content-Type"),
|
||||
File: c.Request.Body,
|
||||
Size: fileSize,
|
||||
Name: c.Request.Header.Get("X-FileName"),
|
||||
VirtualPath: util.DotPathToStandardPath(c.Request.Header.Get("X-Path")),
|
||||
}
|
||||
user, _ := c.Get("user")
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue