|
|
@ -2,11 +2,9 @@ package filesystem
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
"archive/zip"
|
|
|
|
"archive/zip"
|
|
|
|
"bytes"
|
|
|
|
|
|
|
|
"context"
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
|
|
|
|
"os"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"path/filepath"
|
|
|
@ -18,8 +16,7 @@ import (
|
|
|
|
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
|
|
|
|
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx"
|
|
|
|
"github.com/cloudreve/Cloudreve/v3/pkg/util"
|
|
|
|
"github.com/cloudreve/Cloudreve/v3/pkg/util"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"golang.org/x/text/encoding/simplifiedchinese"
|
|
|
|
"github.com/mholt/archiver/v4"
|
|
|
|
"golang.org/x/text/transform"
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
/* ===============
|
|
|
|
/* ===============
|
|
|
@ -206,21 +203,41 @@ func (fs *FileSystem) Decompress(ctx context.Context, src, dst string) error {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
defer zipFile.Close()
|
|
|
|
defer zipFile.Close()
|
|
|
|
|
|
|
|
|
|
|
|
_, err = io.Copy(zipFile, fileStream)
|
|
|
|
// 下载前先判断是否是可解压的格式
|
|
|
|
|
|
|
|
format, readStream, err := archiver.Identify(fs.FileTarget[0].SourceName, fileStream)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
util.Log().Warning("无法写入临时压缩文件 %s , %s", tempZipFilePath, err)
|
|
|
|
util.Log().Warning("无法识别文件格式 %s , %s", fs.FileTarget[0].SourceName, err)
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
zipFile.Close()
|
|
|
|
extractor, ok := format.(archiver.Extractor)
|
|
|
|
fileStream.Close()
|
|
|
|
if !ok {
|
|
|
|
|
|
|
|
return fmt.Errorf("file not an extractor %s", fs.FileTarget[0].SourceName)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解压缩文件
|
|
|
|
// 只有zip格式可以多个文件同时上传
|
|
|
|
r, err := zip.OpenReader(tempZipFilePath)
|
|
|
|
var isZip bool
|
|
|
|
|
|
|
|
switch extractor.(type) {
|
|
|
|
|
|
|
|
case archiver.Zip:
|
|
|
|
|
|
|
|
extractor = archiver.Zip{TextEncoding: "gb18030"}
|
|
|
|
|
|
|
|
isZip = true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 除了zip必须下载到本地,其余的可以边下载边解压
|
|
|
|
|
|
|
|
reader := readStream
|
|
|
|
|
|
|
|
if isZip {
|
|
|
|
|
|
|
|
_, err = io.Copy(zipFile, readStream)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
util.Log().Warning("无法写入临时压缩文件 %s , %s", tempZipFilePath, err)
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
defer r.Close()
|
|
|
|
|
|
|
|
|
|
|
|
fileStream.Close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置文件偏移量
|
|
|
|
|
|
|
|
zipFile.Seek(0, io.SeekStart)
|
|
|
|
|
|
|
|
reader = zipFile
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重设存储策略
|
|
|
|
// 重设存储策略
|
|
|
|
fs.Policy = &fs.User.Policy
|
|
|
|
fs.Policy = &fs.User.Policy
|
|
|
@ -236,50 +253,20 @@ func (fs *FileSystem) Decompress(ctx context.Context, src, dst string) error {
|
|
|
|
worker <- i
|
|
|
|
worker <- i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, f := range r.File {
|
|
|
|
// 上传文件函数
|
|
|
|
fileName := f.Name
|
|
|
|
uploadFunc := func(fileStream io.ReadCloser, size int64, savePath, rawPath string) {
|
|
|
|
// 处理非UTF-8编码
|
|
|
|
|
|
|
|
if f.NonUTF8 {
|
|
|
|
|
|
|
|
i := bytes.NewReader([]byte(fileName))
|
|
|
|
|
|
|
|
decoder := transform.NewReader(i, simplifiedchinese.GB18030.NewDecoder())
|
|
|
|
|
|
|
|
content, _ := ioutil.ReadAll(decoder)
|
|
|
|
|
|
|
|
fileName = string(content)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rawPath := util.FormSlash(fileName)
|
|
|
|
|
|
|
|
savePath := path.Join(dst, rawPath)
|
|
|
|
|
|
|
|
// 路径是否合法
|
|
|
|
|
|
|
|
if !strings.HasPrefix(savePath, util.FillSlash(path.Clean(dst))) {
|
|
|
|
|
|
|
|
return fmt.Errorf("%s: illegal file path", f.Name)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 如果是目录
|
|
|
|
|
|
|
|
if f.FileInfo().IsDir() {
|
|
|
|
|
|
|
|
fs.CreateDirectory(ctx, savePath)
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 上传文件
|
|
|
|
|
|
|
|
fileStream, err := f.Open()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
util.Log().Warning("无法打开压缩包内文件%s , %s , 跳过", rawPath, err)
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
|
|
|
case <-worker:
|
|
|
|
|
|
|
|
wg.Add(1)
|
|
|
|
|
|
|
|
go func(fileStream io.ReadCloser, size int64) {
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
defer func() {
|
|
|
|
|
|
|
|
if isZip {
|
|
|
|
worker <- 1
|
|
|
|
worker <- 1
|
|
|
|
wg.Done()
|
|
|
|
wg.Done()
|
|
|
|
|
|
|
|
}
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
util.Log().Warning("上传压缩包内文件时出错")
|
|
|
|
util.Log().Warning("上传压缩包内文件时出错")
|
|
|
|
fmt.Println(err)
|
|
|
|
fmt.Println(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
err = fs.UploadFromStream(ctx, &fsctx.FileStream{
|
|
|
|
err := fs.UploadFromStream(ctx, &fsctx.FileStream{
|
|
|
|
File: fileStream,
|
|
|
|
File: fileStream,
|
|
|
|
Size: uint64(size),
|
|
|
|
Size: uint64(size),
|
|
|
|
Name: path.Base(savePath),
|
|
|
|
Name: path.Base(savePath),
|
|
|
@ -289,11 +276,41 @@ func (fs *FileSystem) Decompress(ctx context.Context, src, dst string) error {
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
util.Log().Debug("无法上传压缩包内的文件%s , %s , 跳过", rawPath, err)
|
|
|
|
util.Log().Debug("无法上传压缩包内的文件%s , %s , 跳过", rawPath, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}(fileStream, f.FileInfo().Size())
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 解压缩文件,回调函数如果出错会停止解压的下一步进行,全部return nil
|
|
|
|
|
|
|
|
err = extractor.Extract(ctx, reader, nil, func(ctx context.Context, f archiver.File) error {
|
|
|
|
|
|
|
|
rawPath := util.FormSlash(f.NameInArchive)
|
|
|
|
|
|
|
|
savePath := path.Join(dst, rawPath)
|
|
|
|
|
|
|
|
// 路径是否合法
|
|
|
|
|
|
|
|
if !strings.HasPrefix(savePath, util.FillSlash(path.Clean(dst))) {
|
|
|
|
|
|
|
|
util.Log().Warning("%s: illegal file path", f.NameInArchive)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
|
|
|
|
// 如果是目录
|
|
|
|
|
|
|
|
if f.FileInfo.IsDir() {
|
|
|
|
|
|
|
|
fs.CreateDirectory(ctx, savePath)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 上传文件
|
|
|
|
|
|
|
|
fileStream, err := f.Open()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
util.Log().Warning("无法打开压缩包内文件%s , %s , 跳过", rawPath, err)
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !isZip {
|
|
|
|
|
|
|
|
uploadFunc(fileStream, f.FileInfo.Size(), savePath, rawPath)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
<-worker
|
|
|
|
|
|
|
|
wg.Add(1)
|
|
|
|
|
|
|
|
go uploadFunc(fileStream, f.FileInfo.Size(), savePath, rawPath)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|