mirror of https://github.com/cloudreve/Cloudreve
Feat: create hidden file when creating upload session
parent
868a88e5fc
commit
e37e93a7b6
|
@ -352,7 +352,7 @@ func (handler Driver) Token(ctx context.Context, ttl int64, uploadSession *seria
|
||||||
[]interface{}{"content-length-range", 0, handler.Policy.MaxSize})
|
[]interface{}{"content-length-range", 0, handler.Policy.MaxSize})
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := handler.getUploadCredential(ctx, postPolicy, keyTime)
|
res, err := handler.getUploadCredential(ctx, postPolicy, keyTime, file.GetSavePath())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
res.Callback = apiURL
|
res.Callback = apiURL
|
||||||
res.Key = uploadSession.Key
|
res.Key = uploadSession.Key
|
||||||
|
@ -375,13 +375,7 @@ func (handler Driver) Meta(ctx context.Context, path string) (*MetaData, error)
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler Driver) getUploadCredential(ctx context.Context, policy UploadPolicy, keyTime string) (serializer.UploadCredential, error) {
|
func (handler Driver) getUploadCredential(ctx context.Context, policy UploadPolicy, keyTime string, savePath string) (serializer.UploadCredential, error) {
|
||||||
// 读取上下文中生成的存储路径
|
|
||||||
savePath, ok := ctx.Value(fsctx.SavePathCtx).(string)
|
|
||||||
if !ok {
|
|
||||||
return serializer.UploadCredential{}, errors.New("无法获取存储路径")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编码上传策略
|
// 编码上传策略
|
||||||
policyJSON, err := json.Marshal(policy)
|
policyJSON, err := json.Marshal(policy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -88,7 +88,7 @@ func (handler Driver) Put(ctx context.Context, file fsctx.FileHeader) error {
|
||||||
dst := util.RelativePath(filepath.FromSlash(file.GetSavePath()))
|
dst := util.RelativePath(filepath.FromSlash(file.GetSavePath()))
|
||||||
|
|
||||||
// 如果非 Overwrite,则检查是否有重名冲突
|
// 如果非 Overwrite,则检查是否有重名冲突
|
||||||
if file.GetMode() != fsctx.Overwrite {
|
if file.GetMode() == fsctx.Create {
|
||||||
if util.Exists(dst) {
|
if util.Exists(dst) {
|
||||||
util.Log().Warning("物理同名文件已存在或不可用: %s", dst)
|
util.Log().Warning("物理同名文件已存在或不可用: %s", dst)
|
||||||
return errors.New("物理同名文件已存在或不可用")
|
return errors.New("物理同名文件已存在或不可用")
|
||||||
|
|
|
@ -260,7 +260,7 @@ func (client *Client) UploadChunk(ctx context.Context, uploadURL string, chunk *
|
||||||
func (client *Client) Upload(ctx context.Context, file fsctx.FileHeader) error {
|
func (client *Client) Upload(ctx context.Context, file fsctx.FileHeader) error {
|
||||||
// 决定是否覆盖文件
|
// 决定是否覆盖文件
|
||||||
overwrite := "replace"
|
overwrite := "replace"
|
||||||
if file.GetMode() != fsctx.Overwrite {
|
if file.GetMode() != fsctx.Create {
|
||||||
overwrite = "fail"
|
overwrite = "fail"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -237,7 +237,7 @@ func (handler Driver) Put(ctx context.Context, file fsctx.FileHeader) error {
|
||||||
|
|
||||||
// 是否允许覆盖
|
// 是否允许覆盖
|
||||||
overwrite := true
|
overwrite := true
|
||||||
if file.GetMode() != fsctx.Overwrite {
|
if file.GetMode() == fsctx.Create {
|
||||||
overwrite = false
|
overwrite = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,16 +424,10 @@ func (handler Driver) Token(ctx context.Context, ttl int64, uploadSession *seria
|
||||||
[]interface{}{"content-length-range", 0, handler.Policy.MaxSize})
|
[]interface{}{"content-length-range", 0, handler.Policy.MaxSize})
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler.getUploadCredential(ctx, postPolicy, callbackPolicy, ttl)
|
return handler.getUploadCredential(ctx, postPolicy, callbackPolicy, ttl, file.GetSavePath())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler Driver) getUploadCredential(ctx context.Context, policy UploadPolicy, callback CallbackPolicy, TTL int64) (serializer.UploadCredential, error) {
|
func (handler Driver) getUploadCredential(ctx context.Context, policy UploadPolicy, callback CallbackPolicy, TTL int64, savePath string) (serializer.UploadCredential, error) {
|
||||||
// 读取上下文中生成的存储路径
|
|
||||||
savePath, ok := ctx.Value(fsctx.SavePathCtx).(string)
|
|
||||||
if !ok {
|
|
||||||
return serializer.UploadCredential{}, errors.New("无法获取存储路径")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理回调策略
|
// 处理回调策略
|
||||||
callbackPolicyEncoded := ""
|
callbackPolicyEncoded := ""
|
||||||
if callback.CallbackURL != "" {
|
if callback.CallbackURL != "" {
|
||||||
|
|
|
@ -156,7 +156,7 @@ func (handler Driver) Put(ctx context.Context, file fsctx.FileHeader) error {
|
||||||
|
|
||||||
// 决定是否要禁用文件覆盖
|
// 决定是否要禁用文件覆盖
|
||||||
overwrite := "true"
|
overwrite := "true"
|
||||||
if file.GetMode() != fsctx.Overwrite {
|
if file.GetMode() != fsctx.Create {
|
||||||
overwrite = "false"
|
overwrite = "false"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -349,7 +349,7 @@ func (handler Driver) Token(ctx context.Context, ttl int64, uploadSession *seria
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成上传凭证
|
// 生成上传凭证
|
||||||
return handler.getUploadCredential(ctx, putPolicy, apiURL)
|
return handler.getUploadCredential(ctx, putPolicy, apiURL, file.GetSavePath())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Meta 获取文件信息
|
// Meta 获取文件信息
|
||||||
|
@ -376,13 +376,7 @@ func (handler Driver) Meta(ctx context.Context, path string) (*MetaData, error)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler Driver) getUploadCredential(ctx context.Context, policy UploadPolicy, callback *url.URL) (serializer.UploadCredential, error) {
|
func (handler Driver) getUploadCredential(ctx context.Context, policy UploadPolicy, callback *url.URL, savePath string) (serializer.UploadCredential, error) {
|
||||||
|
|
||||||
// 读取上下文中生成的存储路径和文件大小
|
|
||||||
savePath, ok := ctx.Value(fsctx.SavePathCtx).(string)
|
|
||||||
if !ok {
|
|
||||||
return serializer.UploadCredential{}, errors.New("无法获取存储路径")
|
|
||||||
}
|
|
||||||
|
|
||||||
longDate := time.Now().UTC().Format("20060102T150405Z")
|
longDate := time.Now().UTC().Format("20060102T150405Z")
|
||||||
shortDate := time.Now().UTC().Format("20060102")
|
shortDate := time.Now().UTC().Format("20060102")
|
||||||
|
|
|
@ -5,10 +5,6 @@ type key int
|
||||||
const (
|
const (
|
||||||
// GinCtx Gin的上下文
|
// GinCtx Gin的上下文
|
||||||
GinCtx key = iota
|
GinCtx key = iota
|
||||||
// SavePathCtx 文件物理路径
|
|
||||||
SavePathCtx
|
|
||||||
// FileHeaderCtx 上传的文件
|
|
||||||
FileHeaderCtx
|
|
||||||
// PathCtx 文件或目录的虚拟路径
|
// PathCtx 文件或目录的虚拟路径
|
||||||
PathCtx
|
PathCtx
|
||||||
// FileModelCtx 文件数据库模型
|
// FileModelCtx 文件数据库模型
|
||||||
|
|
|
@ -9,8 +9,10 @@ type WriteMode int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Overwrite WriteMode = iota
|
Overwrite WriteMode = iota
|
||||||
|
// Append 只适用于本地策略
|
||||||
Append
|
Append
|
||||||
Create
|
Create
|
||||||
|
Nop
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileStream 用户传来的文件
|
// FileStream 用户传来的文件
|
||||||
|
@ -71,6 +73,10 @@ func (file *FileStream) GetSavePath() string {
|
||||||
return file.SavePath
|
return file.SavePath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (file *FileStream) SetSize(size uint64) {
|
||||||
|
file.Size = size
|
||||||
|
}
|
||||||
|
|
||||||
// FileHeader 上传来的文件数据处理器
|
// FileHeader 上传来的文件数据处理器
|
||||||
type FileHeader interface {
|
type FileHeader interface {
|
||||||
io.Reader
|
io.Reader
|
||||||
|
@ -84,4 +90,5 @@ type FileHeader interface {
|
||||||
GetLastModified() time.Time
|
GetLastModified() time.Time
|
||||||
IsHidden() bool
|
IsHidden() bool
|
||||||
GetSavePath() string
|
GetSavePath() string
|
||||||
|
SetSize(uint64)
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,7 +284,7 @@ func GenericAfterUpload(ctx context.Context, fs *FileSystem, fileHeader fsctx.Fi
|
||||||
// 检查文件是否存在
|
// 检查文件是否存在
|
||||||
if ok, _ := fs.IsChildFileExist(
|
if ok, _ := fs.IsChildFileExist(
|
||||||
folder,
|
folder,
|
||||||
ctx.Value(fsctx.FileHeaderCtx).(fsctx.FileHeader).GetFileName(),
|
fileHeader.GetFileName(),
|
||||||
); ok {
|
); ok {
|
||||||
return ErrFileExisted
|
return ErrFileExisted
|
||||||
}
|
}
|
||||||
|
@ -307,3 +307,9 @@ func GenericAfterUpload(ctx context.Context, fs *FileSystem, fileHeader fsctx.Fi
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HookClearFileHeaderSize 将FileHeader大小设定为0
|
||||||
|
func HookClearFileHeaderSize(ctx context.Context, fs *FileSystem, fileHeader fsctx.FileHeader) error {
|
||||||
|
fileHeader.SetSize(0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -350,20 +350,22 @@ func (fs *FileSystem) listObjects(ctx context.Context, parent string, files []mo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newFile := serializer.Object{
|
if !file.Hidden {
|
||||||
ID: hashid.HashID(file.ID, hashid.FileID),
|
newFile := serializer.Object{
|
||||||
Name: file.Name,
|
ID: hashid.HashID(file.ID, hashid.FileID),
|
||||||
Path: processedPath,
|
Name: file.Name,
|
||||||
Pic: file.PicInfo,
|
Path: processedPath,
|
||||||
Size: file.Size,
|
Pic: file.PicInfo,
|
||||||
Type: "file",
|
Size: file.Size,
|
||||||
Date: file.CreatedAt,
|
Type: "file",
|
||||||
SourceEnabled: file.GetPolicy().IsOriginLinkEnable,
|
Date: file.CreatedAt,
|
||||||
|
SourceEnabled: file.GetPolicy().IsOriginLinkEnable,
|
||||||
|
}
|
||||||
|
if shareKey != "" {
|
||||||
|
newFile.Key = shareKey
|
||||||
|
}
|
||||||
|
objects = append(objects, newFile)
|
||||||
}
|
}
|
||||||
if shareKey != "" {
|
|
||||||
newFile.Key = shareKey
|
|
||||||
}
|
|
||||||
objects = append(objects, newFile)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return objects
|
return objects
|
||||||
|
|
|
@ -20,10 +20,12 @@ import (
|
||||||
================
|
================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const (
|
||||||
|
UploadSessionMetaKey = "upload_session"
|
||||||
|
)
|
||||||
|
|
||||||
// Upload 上传文件
|
// Upload 上传文件
|
||||||
func (fs *FileSystem) Upload(ctx context.Context, file *fsctx.FileStream) (err error) {
|
func (fs *FileSystem) Upload(ctx context.Context, file *fsctx.FileStream) (err error) {
|
||||||
ctx = context.WithValue(ctx, fsctx.FileHeaderCtx, file)
|
|
||||||
|
|
||||||
// 上传前的钩子
|
// 上传前的钩子
|
||||||
err = fs.Trigger(ctx, "BeforeUpload", file)
|
err = fs.Trigger(ctx, "BeforeUpload", file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -45,10 +47,12 @@ func (fs *FileSystem) Upload(ctx context.Context, file *fsctx.FileStream) (err e
|
||||||
go fs.CancelUpload(ctx, savePath, file)
|
go fs.CancelUpload(ctx, savePath, file)
|
||||||
|
|
||||||
// 保存文件
|
// 保存文件
|
||||||
err = fs.Handler.Put(ctx, file)
|
if file.Mode != fsctx.Nop {
|
||||||
if err != nil {
|
err = fs.Handler.Put(ctx, file)
|
||||||
fs.Trigger(ctx, "AfterUploadFailed", file)
|
if err != nil {
|
||||||
return err
|
fs.Trigger(ctx, "AfterUploadFailed", file)
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传完成后的钩子
|
// 上传完成后的钩子
|
||||||
|
@ -150,28 +154,26 @@ func (fs *FileSystem) CreateUploadSession(ctx context.Context, path string, size
|
||||||
credentialTTL := model.GetIntSetting("upload_credential_timeout", 3600)
|
credentialTTL := model.GetIntSetting("upload_credential_timeout", 3600)
|
||||||
callBackSessionTTL := model.GetIntSetting("upload_session_timeout", 86400)
|
callBackSessionTTL := model.GetIntSetting("upload_session_timeout", 86400)
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// 进行文件上传预检查
|
|
||||||
|
|
||||||
file := &fsctx.FileStream{
|
|
||||||
Size: size,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查上传请求合法性
|
|
||||||
if err := HookValidateFile(ctx, fs, file); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := HookValidateCapacityWithoutIncrease(ctx, fs, file); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成存储路径
|
|
||||||
savePath := fs.GenerateSavePath(ctx, &fsctx.FileStream{Name: name, VirtualPath: path})
|
|
||||||
|
|
||||||
callbackKey := uuid.Must(uuid.NewV4()).String()
|
callbackKey := uuid.Must(uuid.NewV4()).String()
|
||||||
|
|
||||||
|
// 创建隐藏的文件,同时校验文件信息
|
||||||
|
file := &fsctx.FileStream{
|
||||||
|
Size: size,
|
||||||
|
Name: name,
|
||||||
|
VirtualPath: path,
|
||||||
|
Mode: fsctx.Nop,
|
||||||
|
Hidden: true,
|
||||||
|
Metadata: map[string]string{
|
||||||
|
UploadSessionMetaKey: callbackKey,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fs.Use("BeforeUpload", HookValidateFile)
|
||||||
|
fs.Use("AfterUpload", HookClearFileHeaderSize)
|
||||||
|
fs.Use("AfterUpload", GenericAfterUpload)
|
||||||
|
if err := fs.Upload(ctx, file); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
uploadSession := &serializer.UploadSession{
|
uploadSession := &serializer.UploadSession{
|
||||||
Key: callbackKey,
|
Key: callbackKey,
|
||||||
UID: fs.User.ID,
|
UID: fs.User.ID,
|
||||||
|
@ -179,7 +181,7 @@ func (fs *FileSystem) CreateUploadSession(ctx context.Context, path string, size
|
||||||
VirtualPath: path,
|
VirtualPath: path,
|
||||||
Name: name,
|
Name: name,
|
||||||
Size: size,
|
Size: size,
|
||||||
SavePath: savePath,
|
SavePath: file.SavePath,
|
||||||
ChunkSize: fs.Policy.OptionsSerialized.ChunkSize,
|
ChunkSize: fs.Policy.OptionsSerialized.ChunkSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue