mirror of
				https://github.com/cloudreve/cloudreve.git
				synced 2025-10-31 16:49:03 +08:00 
			
		
		
		
	使用archiver对压缩文件进行解压
This commit is contained in:
		| @ -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) | ||||||
| 	// 解压缩文件 | 	} | ||||||
| 	r, err := zip.OpenReader(tempZipFilePath) |  | ||||||
| 	if err != nil { | 	// 只有zip格式可以多个文件同时上传 | ||||||
| 		return err | 	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 { | ||||||
|  | 			util.Log().Warning("无法写入临时压缩文件 %s , %s", tempZipFilePath, err) | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fileStream.Close() | ||||||
|  |  | ||||||
|  | 		// 设置文件偏移量 | ||||||
|  | 		zipFile.Seek(0, io.SeekStart) | ||||||
|  | 		reader = zipFile | ||||||
| 	} | 	} | ||||||
| 	defer r.Close() |  | ||||||
|  |  | ||||||
| 	// 重设存储策略 | 	// 重设存储策略 | ||||||
| 	fs.Policy = &fs.User.Policy | 	fs.Policy = &fs.User.Policy | ||||||
| @ -236,64 +253,64 @@ 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编码 | 		defer func() { | ||||||
| 		if f.NonUTF8 { | 			if isZip { | ||||||
| 			i := bytes.NewReader([]byte(fileName)) | 				worker <- 1 | ||||||
| 			decoder := transform.NewReader(i, simplifiedchinese.GB18030.NewDecoder()) | 				wg.Done() | ||||||
| 			content, _ := ioutil.ReadAll(decoder) | 			} | ||||||
| 			fileName = string(content) | 			if err := recover(); err != nil { | ||||||
| 		} | 				util.Log().Warning("上传压缩包内文件时出错") | ||||||
|  | 				fmt.Println(err) | ||||||
|  | 			} | ||||||
|  | 		}() | ||||||
|  |  | ||||||
| 		rawPath := util.FormSlash(fileName) | 		err := fs.UploadFromStream(ctx, &fsctx.FileStream{ | ||||||
|  | 			File:        fileStream, | ||||||
|  | 			Size:        uint64(size), | ||||||
|  | 			Name:        path.Base(savePath), | ||||||
|  | 			VirtualPath: path.Dir(savePath), | ||||||
|  | 		}, true) | ||||||
|  | 		fileStream.Close() | ||||||
|  | 		if err != nil { | ||||||
|  | 			util.Log().Debug("无法上传压缩包内的文件%s , %s , 跳过", rawPath, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 解压缩文件,回调函数如果出错会停止解压的下一步进行,全部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) | 		savePath := path.Join(dst, rawPath) | ||||||
| 		// 路径是否合法 | 		// 路径是否合法 | ||||||
| 		if !strings.HasPrefix(savePath, util.FillSlash(path.Clean(dst))) { | 		if !strings.HasPrefix(savePath, util.FillSlash(path.Clean(dst))) { | ||||||
| 			return fmt.Errorf("%s: illegal file path", f.Name) | 			util.Log().Warning("%s: illegal file path", f.NameInArchive) | ||||||
|  | 			return nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// 如果是目录 | 		// 如果是目录 | ||||||
| 		if f.FileInfo().IsDir() { | 		if f.FileInfo.IsDir() { | ||||||
| 			fs.CreateDirectory(ctx, savePath) | 			fs.CreateDirectory(ctx, savePath) | ||||||
| 			continue | 			return nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// 上传文件 | 		// 上传文件 | ||||||
| 		fileStream, err := f.Open() | 		fileStream, err := f.Open() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			util.Log().Warning("无法打开压缩包内文件%s , %s , 跳过", rawPath, err) | 			util.Log().Warning("无法打开压缩包内文件%s , %s , 跳过", rawPath, err) | ||||||
| 			continue | 			return nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		select { | 		if !isZip { | ||||||
| 		case <-worker: | 			uploadFunc(fileStream, f.FileInfo.Size(), savePath, rawPath) | ||||||
|  | 		} else { | ||||||
|  | 			<-worker | ||||||
| 			wg.Add(1) | 			wg.Add(1) | ||||||
| 			go func(fileStream io.ReadCloser, size int64) { | 			go uploadFunc(fileStream, f.FileInfo.Size(), savePath, rawPath) | ||||||
| 				defer func() { |  | ||||||
| 					worker <- 1 |  | ||||||
| 					wg.Done() |  | ||||||
| 					if err := recover(); err != nil { |  | ||||||
| 						util.Log().Warning("上传压缩包内文件时出错") |  | ||||||
| 						fmt.Println(err) |  | ||||||
| 					} |  | ||||||
| 				}() |  | ||||||
|  |  | ||||||
| 				err = fs.UploadFromStream(ctx, &fsctx.FileStream{ |  | ||||||
| 					File:        fileStream, |  | ||||||
| 					Size:        uint64(size), |  | ||||||
| 					Name:        path.Base(savePath), |  | ||||||
| 					VirtualPath: path.Dir(savePath), |  | ||||||
| 				}, true) |  | ||||||
| 				fileStream.Close() |  | ||||||
| 				if err != nil { |  | ||||||
| 					util.Log().Debug("无法上传压缩包内的文件%s , %s , 跳过", rawPath, err) |  | ||||||
| 				} |  | ||||||
| 			}(fileStream, f.FileInfo().Size()) |  | ||||||
| 		} | 		} | ||||||
|  | 		return nil | ||||||
| 	} | 	}) | ||||||
| 	wg.Wait() | 	wg.Wait() | ||||||
| 	return nil | 	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) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// 创建任务 | 	// 创建任务 | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Weidi Deng
					Weidi Deng