最近项目需求,需要前端页面将压缩文件传递打后台,然后后台再解压文件,对文件进行逐一进行处理。刚开始实现这个技术的时候本来想使用原生的库来进行解压处理,后面想着找找有造好的轮子应该可以拿来用,就在github上进行了搜寻,最后找到一个可以用的轮子,名字为archiver,github地址为:https://github.com/mholt/archiver
1、介绍
此开源轮子支持rar,zip,7z等压缩格式,其他的压缩格式可以在github上的详情进行查看,因为我目前暂时只需要rar和zip的解压方式,7z格式虽然说是支持,但是我试过,解压的时候会报错,不支持该格式的压缩文件。不知道什么原因,我去issues提过问题,他们也没给个让人明白的答案,仿佛是支持,但是我测试出来是不支持的。因为我只需要解压功能,所以下面我只演示解压的使用方法,压缩的方法可以通过github自行测试研究。
2、使用方法
我测试代码的go版本使用的使用的是1.18.3版本,测试代码如下所示,比较简单,一看就能明白:
package main
import (
"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 main
import (
"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版本过低,没有亲自验证。还有就是如果你需要处文件的代码比较多的话,可以将回调函数写在外面,结构性要更强一些。