最近项目需求,需要前端页面将压缩文件传递打后台,然后后台再解压文件,对文件进行逐一进行处理。刚开始实现这个技术的时候本来想使用原生的库来进行解压处理,后面想着找找有造好的轮子应该可以拿来用,就在github上进行了搜寻,最后找到一个可以用的轮子,名字为archiver,github地址为:https://github.com/mholt/archiver
1、介绍
此开源轮子支持rar,zip,7z等压缩格式,其他的压缩格式可以在github上的详情进行查看,因为我目前暂时只需要rar和zip的解压方式,7z格式虽然说是支持,但是我试过,解压的时候会报错,不支持该格式的压缩文件。不知道什么原因,我去issues提过问题,他们也没给个让人明白的答案,仿佛是支持,但是我测试出来是不支持的。因为我只需要解压功能,所以下面我只演示解压的使用方法,压缩的方法可以通过github自行测试研究。
2、使用方法
我测试代码的go版本使用的使用的是1.18.3版本,测试代码如下所示,比较简单,一看就能明白:
package mainimport ("context""fmt""io""os""github.com/mholt/archiver/v4"
)func main() {ArchiverTest("public.zip")
}// archiver解压压缩包
func ArchiverTest(path string) {f, _ := os.Open(path)format, readStream, err := archiver.Identify(path, f)if err != nil {return}extractor, ok := format.(archiver.Extractor)if !ok {return}switch extractor.(type) {case archiver.Zip:extractor = archiver.Zip{TextEncoding: "gbk"}fmt.Println("archiver.Zip")case archiver.SevenZip:extractor = archiver.SevenZip{}fmt.Println("archiver.SevenZip")case archiver.Rar:extractor = archiver.Rar{}fmt.Println("archiver.Rar")default:fmt.Println("unsupported compression algorithm")return}//fileList := []string{"file1.txt", "subfolder"}ctx := context.Background()handler := func(ctx context.Context, f archiver.File) error {filename := f.Name()newfile, err := os.Create(filename)if err != nil {panic(err)}defer newfile.Close()old, err := f.Open()if err != nil {panic(err)}defer old.Close()_, err = io.Copy(newfile, old)if err != nil {panic(err)}fmt.Printf("extracted %s \n", f.Name())return nil}err = extractor.Extract(ctx, readStream, nil, handler)if err != nil {return}}
- 代码中可以看到通过一个解压函数将压缩文件传入,然后进行解压
- 传入后,会对传入的压缩文件进行分类,代码中我分为了zip、rar和7z的压缩方式
- err = extractor.Extract(ctx, readStream, nil, handler)的handler是前面定义的函数,属于回调函数,回调函数是对每一个解压出来的文件进行处理,代码示例中的操作是将压缩文件解压出来,然后保存到本地目录,然后还打印了解压后的文件名字
- 回调函数是阻塞型的函数,里面没有调用携程来进行处理,所以回调函数要把解压后的所有文件处理完成后,该函数才会返回
3、回调函数参数传递
如果需要传递参数到回调函数的话,可以使用如下代码进行传递参数:
package mainimport ("context""fmt""io""os""github.com/mholt/archiver/v4"
)type jellyString struct{}func main() {ArchiverTest("public.zip")
}// archiver解压压缩包
func ArchiverTest(path string) {f, _ := os.Open(path)format, readStream, err := archiver.Identify(path, f)if err != nil {return}extractor, ok := format.(archiver.Extractor)if !ok {return}switch extractor.(type) {case archiver.Zip:extractor = archiver.Zip{TextEncoding: "gbk"}fmt.Println("archiver.Zip")case archiver.SevenZip:extractor = archiver.SevenZip{}fmt.Println("archiver.SevenZip")case archiver.Rar:extractor = archiver.Rar{}fmt.Println("archiver.Rar")default:fmt.Println("unsupported compression algorithm")return}//fileList := []string{"file1.txt", "subfolder"}handler := func(ctx context.Context, f archiver.File) error {inputdata := ctx.Value(jellyString{}).(string)fmt.Printf("inputdata %s \n", inputdata )filename := f.Name()newfile, err := os.Create(filename)if err != nil {panic(err)}defer newfile.Close()old, err := f.Open()if err != nil {panic(err)}defer old.Close()_, err = io.Copy(newfile, old)if err != nil {panic(err)}fmt.Printf("extracted %s \n", f.Name())return nil}username := "传递参数用户名username"ctx := context.Background()ctxValue := context.WithValue(ctx, jellyString{}, username)err = extractor.Extract(ctxValue , readStream, nil, handler)if err != nil {return}}
传参数用的是一个结构体参数,据说是比较安全的传递参数的方法,我也按照这种建议的方式进行的参数传递,单个参数和结构体参数应该都可以传递。
4、小结
解压文件我测试后还是可以针对rar和zip进行解压,并完整的将文件保存下来的,7z我测试无法成功,有成功的同学可以评论区分享,我怀疑可能是我go版本过低,没有亲自验证。还有就是如果你需要处文件的代码比较多的话,可以将回调函数写在外面,结构性要更强一些。