使用archiver对压缩文件进行解压

pull/1232/head
Weidi Deng 3 years ago
parent 7a3d44451b
commit 23bd1389bc

@ -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
} }

@ -127,9 +127,19 @@ func (service *ItemDecompressService) CreateDecompressTask(c *gin.Context) seria
return serializer.Err(serializer.CodeParamErr, "文件太大", nil) return serializer.Err(serializer.CodeParamErr, "文件太大", nil)
} }
// 必须是zip压缩包 // 支持的压缩格式后缀
if !strings.HasSuffix(file.Name, ".zip") { var (
return serializer.Err(serializer.CodeParamErr, "只能解压 ZIP 格式的压缩文件", nil) suffixes = []string{".zip", ".gz", ".xz", ".tar", ".rar"}
matched bool
)
for _, suffix := range suffixes {
if strings.HasSuffix(file.Name, suffix) {
matched = true
break
}
}
if !matched {
return serializer.Err(serializer.CodeParamErr, "不支持该格式的压缩文件", nil)
} }
// 创建任务 // 创建任务

Loading…
Cancel
Save