Feat: file uploading in slave mode

pull/247/head
HFO4 5 years ago
parent 6470340104
commit 132c7a8fcb

@ -129,7 +129,7 @@ func (policy *Policy) GenerateFileName(uid uint, origin string) string {
case "qiniu": case "qiniu":
// 七牛会将$(fname)自动替换为原始文件名 // 七牛会将$(fname)自动替换为原始文件名
replaceTable["{originname}"] = "$(fname)" replaceTable["{originname}"] = "$(fname)"
case "local": case "local", "remote":
replaceTable["{originname}"] = origin replaceTable["{originname}"] = origin
case "oss": case "oss":
// OSS会将${filename}自动替换为原始文件名 // OSS会将${filename}自动替换为原始文件名

@ -54,7 +54,6 @@ func HookIsFileExist(ctx context.Context, fs *FileSystem) error {
} }
// HookSlaveUploadValidate Slave模式下对文件上传的一系列验证 // HookSlaveUploadValidate Slave模式下对文件上传的一系列验证
// TODO 测试
func HookSlaveUploadValidate(ctx context.Context, fs *FileSystem) error { func HookSlaveUploadValidate(ctx context.Context, fs *FileSystem) error {
file := ctx.Value(fsctx.FileHeaderCtx).(FileHeader) file := ctx.Value(fsctx.FileHeaderCtx).(FileHeader)
policy := ctx.Value(fsctx.UploadPolicyCtx).(serializer.UploadPolicy) policy := ctx.Value(fsctx.UploadPolicyCtx).(serializer.UploadPolicy)
@ -209,6 +208,22 @@ func GenericAfterUpdate(ctx context.Context, fs *FileSystem) error {
return nil return nil
} }
// SlaveAfterUpload Slave模式下上传完成钩子
// TODO 测试
func SlaveAfterUpload(ctx context.Context, fs *FileSystem) error {
fileHeader := ctx.Value(fsctx.FileHeaderCtx).(FileHeader)
// 构造一个model.File用于生成缩略图
file := model.File{
Name: fileHeader.GetFileName(),
SourceName: ctx.Value(fsctx.SavePathCtx).(string),
}
fs.GenerateThumbnail(ctx, &file)
// TODO 发送回调请求
return nil
}
// GenericAfterUpload 文件上传完成后,包含数据库操作 // GenericAfterUpload 文件上传完成后,包含数据库操作
func GenericAfterUpload(ctx context.Context, fs *FileSystem) error { func GenericAfterUpload(ctx context.Context, fs *FileSystem) error {
// 文件存放的虚拟路径 // 文件存放的虚拟路径

@ -72,7 +72,11 @@ func (fs *FileSystem) GenerateThumbnail(ctx context.Context, file *model.File) {
} }
// 更新文件的图像信息 // 更新文件的图像信息
err = file.UpdatePicInfo(fmt.Sprintf("%d,%d", w, h)) if file.Model.ID > 0 {
err = file.UpdatePicInfo(fmt.Sprintf("%d,%d", w, h))
} else {
file.PicInfo = fmt.Sprintf("%d,%d", w, h)
}
// 失败时删除缩略图文件 // 失败时删除缩略图文件
if err != nil { if err != nil {

@ -94,6 +94,26 @@ func TestFileSystem_GenerateThumbnail(t *testing.T) {
mock.ExpectExec("UPDATE(.+)").WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectExec("UPDATE(.+)").WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit() mock.ExpectCommit()
file := &model.File{
Model: gorm.Model{ID: 1},
Name: "123.jpg",
SourceName: "TestFileSystem_GenerateThumbnail.jpeg",
}
fs.GenerateThumbnail(ctx, file)
asserts.NoError(mock.ExpectationsWereMet())
testHandler.AssertExpectations(t)
asserts.True(util.Exists("TestFileSystem_GenerateThumbnail.jpeg" + conf.ThumbConfig.FileSuffix))
}
// 成功,不进行数据库更新
{
src := CreateTestImage()
testHandler := new(FileHeaderMock)
testHandler.On("Get", testMock.Anything, "TestFileSystem_GenerateThumbnail.jpeg").Return(src, nil)
fs.Handler = testHandler
file := &model.File{ file := &model.File{
Name: "123.jpg", Name: "123.jpg",
SourceName: "TestFileSystem_GenerateThumbnail.jpeg", SourceName: "TestFileSystem_GenerateThumbnail.jpeg",
@ -119,6 +139,7 @@ func TestFileSystem_GenerateThumbnail(t *testing.T) {
mock.ExpectRollback() mock.ExpectRollback()
file := &model.File{ file := &model.File{
Model: gorm.Model{ID: 1},
Name: "123.jpg", Name: "123.jpg",
SourceName: "TestFileSystem_GenerateThumbnail.jpeg", SourceName: "TestFileSystem_GenerateThumbnail.jpeg",
} }
@ -132,6 +153,7 @@ func TestFileSystem_GenerateThumbnail(t *testing.T) {
// 不能生成缩略图 // 不能生成缩略图
{ {
file := &model.File{ file := &model.File{
Model: gorm.Model{ID: 1},
Name: "123.123", Name: "123.123",
SourceName: "TestFileSystem_GenerateThumbnail.jpeg", SourceName: "TestFileSystem_GenerateThumbnail.jpeg",
} }

@ -4,6 +4,7 @@ import (
"context" "context"
model "github.com/HFO4/cloudreve/models" model "github.com/HFO4/cloudreve/models"
"github.com/HFO4/cloudreve/pkg/filesystem/fsctx" "github.com/HFO4/cloudreve/pkg/filesystem/fsctx"
"github.com/HFO4/cloudreve/pkg/serializer"
"github.com/HFO4/cloudreve/pkg/util" "github.com/HFO4/cloudreve/pkg/util"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"path/filepath" "path/filepath"
@ -83,16 +84,24 @@ func (fs *FileSystem) GenerateSavePath(ctx context.Context, file FileHeader) str
) )
} }
// 匿名文件系统使用空上传策略生成路径 // 匿名文件系统尝试根据上下文中的上传策略生成路径
nilPolicy := model.Policy{} var anonymousPolicy model.Policy
if policy, ok := ctx.Value(fsctx.UploadPolicyCtx).(serializer.UploadPolicy); ok {
anonymousPolicy = model.Policy{
Type: "remote",
AutoRename: policy.AutoRename,
DirNameRule: policy.SavePath,
FileNameRule: policy.FileName,
}
}
return filepath.Join( return filepath.Join(
nilPolicy.GeneratePath( anonymousPolicy.GeneratePath(
0, 0,
"", "",
), ),
nilPolicy.GenerateFileName( anonymousPolicy.GenerateFileName(
0, 0,
"", file.GetFileName(),
), ),
) )
} }

@ -7,6 +7,7 @@ import (
"github.com/HFO4/cloudreve/pkg/filesystem/fsctx" "github.com/HFO4/cloudreve/pkg/filesystem/fsctx"
"github.com/HFO4/cloudreve/pkg/filesystem/local" "github.com/HFO4/cloudreve/pkg/filesystem/local"
"github.com/HFO4/cloudreve/pkg/filesystem/response" "github.com/HFO4/cloudreve/pkg/filesystem/response"
"github.com/HFO4/cloudreve/pkg/serializer"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -139,3 +140,22 @@ func TestFileSystem_Upload(t *testing.T) {
testHandller2.AssertExpectations(t) testHandller2.AssertExpectations(t)
} }
func TestFileSystem_GenerateSavePath_Anonymous(t *testing.T) {
asserts := assert.New(t)
fs := FileSystem{User: &model.User{}}
ctx := context.WithValue(
context.Background(),
fsctx.UploadPolicyCtx,
serializer.UploadPolicy{
SavePath: "{randomkey16}",
AutoRename: false,
},
)
savePath := fs.GenerateSavePath(ctx, local.FileStream{
Name: "test.test",
})
asserts.Len(savePath, 26)
asserts.Contains(savePath, "test.test")
}

@ -8,6 +8,8 @@ import (
// UploadPolicy slave模式下传递的上传策略 // UploadPolicy slave模式下传递的上传策略
type UploadPolicy struct { type UploadPolicy struct {
SavePath string `json:"save_path"` SavePath string `json:"save_path"`
FileName string `json:"file_name"`
AutoRename bool `json:"auto_rename"`
MaxSize uint64 `json:"max_size"` MaxSize uint64 `json:"max_size"`
AllowedExtension []string `json:"allowed_extension"` AllowedExtension []string `json:"allowed_extension"`
CallbackURL string `json:"callback_url"` CallbackURL string `json:"callback_url"`

@ -24,19 +24,22 @@ func SlaveUpload(c *gin.Context) {
c.JSON(200, serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err)) c.JSON(200, serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err))
return return
} }
fs.Handler = local.Handler{}
// 从请求中取得上传策略 // 从请求中取得上传策略
uploadPolicyRaw := c.GetHeader("X-Policy") uploadPolicyRaw := c.GetHeader("X-Policy")
if uploadPolicyRaw == "" { if uploadPolicyRaw == "" {
c.JSON(200, serializer.ParamErr("未指定上传策略", nil)) c.JSON(200, serializer.ParamErr("未指定上传策略", nil))
return
} }
// 解析上传策略 // 解析上传策略
uploadPolicy, err := serializer.DecodeUploadPolicy(uploadPolicyRaw) uploadPolicy, err := serializer.DecodeUploadPolicy(uploadPolicyRaw)
if err != nil { if err != nil {
c.JSON(200, serializer.ParamErr("上传策略格式有误", err)) c.JSON(200, serializer.ParamErr("上传策略格式有误", err))
return
} }
ctx = context.WithValue(ctx, fsctx.UploadPolicyCtx, uploadPolicy) ctx = context.WithValue(ctx, fsctx.UploadPolicyCtx, *uploadPolicy)
// 取得文件大小 // 取得文件大小
fileSize, err := strconv.ParseUint(c.Request.Header.Get("Content-Length"), 10, 64) fileSize, err := strconv.ParseUint(c.Request.Header.Get("Content-Length"), 10, 64)
@ -62,6 +65,7 @@ func SlaveUpload(c *gin.Context) {
// 给文件系统分配钩子 // 给文件系统分配钩子
fs.Use("BeforeUpload", filesystem.HookSlaveUploadValidate) fs.Use("BeforeUpload", filesystem.HookSlaveUploadValidate)
fs.Use("AfterUploadCanceled", filesystem.HookDeleteTempFile) fs.Use("AfterUploadCanceled", filesystem.HookDeleteTempFile)
fs.Use("AfterUpload", filesystem.SlaveAfterUpload)
fs.Use("AfterValidateFailed", filesystem.HookDeleteTempFile) fs.Use("AfterValidateFailed", filesystem.HookDeleteTempFile)
// 执行上传 // 执行上传

Loading…
Cancel
Save