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 ( | ||||
| 	"archive/zip" | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| @ -18,8 +16,7 @@ import ( | ||||
| 	"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx" | ||||
| 	"github.com/cloudreve/Cloudreve/v3/pkg/util" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"golang.org/x/text/encoding/simplifiedchinese" | ||||
| 	"golang.org/x/text/transform" | ||||
| 	"github.com/mholt/archiver/v4" | ||||
| ) | ||||
|  | ||||
| /* =============== | ||||
| @ -206,21 +203,41 @@ func (fs *FileSystem) Decompress(ctx context.Context, src, dst string) error { | ||||
| 	} | ||||
| 	defer zipFile.Close() | ||||
|  | ||||
| 	_, err = io.Copy(zipFile, fileStream) | ||||
| 	// 下载前先判断是否是可解压的格式 | ||||
| 	format, readStream, err := archiver.Identify(fs.FileTarget[0].SourceName, fileStream) | ||||
| 	if err != nil { | ||||
| 		util.Log().Warning("无法写入临时压缩文件 %s , %s", tempZipFilePath, err) | ||||
| 		util.Log().Warning("无法识别文件格式 %s , %s", fs.FileTarget[0].SourceName, err) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	zipFile.Close() | ||||
| 	fileStream.Close() | ||||
|  | ||||
| 	// 解压缩文件 | ||||
| 	r, err := zip.OpenReader(tempZipFilePath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	extractor, ok := format.(archiver.Extractor) | ||||
| 	if !ok { | ||||
| 		return fmt.Errorf("file not an extractor %s", fs.FileTarget[0].SourceName) | ||||
| 	} | ||||
|  | ||||
| 	// 只有zip格式可以多个文件同时上传 | ||||
| 	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 | ||||
| @ -236,64 +253,64 @@ func (fs *FileSystem) Decompress(ctx context.Context, src, dst string) error { | ||||
| 		worker <- i | ||||
| 	} | ||||
|  | ||||
| 	for _, f := range r.File { | ||||
| 		fileName := f.Name | ||||
| 		// 处理非UTF-8编码 | ||||
| 		if f.NonUTF8 { | ||||
| 			i := bytes.NewReader([]byte(fileName)) | ||||
| 			decoder := transform.NewReader(i, simplifiedchinese.GB18030.NewDecoder()) | ||||
| 			content, _ := ioutil.ReadAll(decoder) | ||||
| 			fileName = string(content) | ||||
| 		} | ||||
| 	// 上传文件函数 | ||||
| 	uploadFunc := func(fileStream io.ReadCloser, size int64, savePath, rawPath string) { | ||||
| 		defer func() { | ||||
| 			if isZip { | ||||
| 				worker <- 1 | ||||
| 				wg.Done() | ||||
| 			} | ||||
| 			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) | ||||
| 		// 路径是否合法 | ||||
| 		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) | ||||
| 			continue | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		// 上传文件 | ||||
| 		fileStream, err := f.Open() | ||||
| 		if err != nil { | ||||
| 			util.Log().Warning("无法打开压缩包内文件%s , %s , 跳过", rawPath, err) | ||||
| 			continue | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		select { | ||||
| 		case <-worker: | ||||
| 		if !isZip { | ||||
| 			uploadFunc(fileStream, f.FileInfo.Size(), savePath, rawPath) | ||||
| 		} else { | ||||
| 			<-worker | ||||
| 			wg.Add(1) | ||||
| 			go func(fileStream io.ReadCloser, size int64) { | ||||
| 				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()) | ||||
| 			go uploadFunc(fileStream, f.FileInfo.Size(), savePath, rawPath) | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| 		return nil | ||||
| 	}) | ||||
| 	wg.Wait() | ||||
| 	return nil | ||||
| 	return err | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -127,9 +127,19 @@ func (service *ItemDecompressService) CreateDecompressTask(c *gin.Context) seria | ||||
| 		return serializer.Err(serializer.CodeParamErr, "文件太大", nil) | ||||
| 	} | ||||
|  | ||||
| 	// 必须是zip压缩包 | ||||
| 	if !strings.HasSuffix(file.Name, ".zip") { | ||||
| 		return serializer.Err(serializer.CodeParamErr, "只能解压 ZIP 格式的压缩文件", nil) | ||||
| 	// 支持的压缩格式后缀 | ||||
| 	var ( | ||||
| 		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