有一种简单的方法可以用golang解压缩文件吗?
现在我的代码是:
func Unzip(src, dest string) error {
r, err := zip.OpenReader(src)
if err != nil {
return err
}
defer r.Close()
for _, f := range r.File {
rc, err := f.Open()
if err != nil {
return err
}
defer rc.Close()
path := filepath.Join(dest, f.Name)
if f.FileInfo().IsDir() {
os.MkdirAll(path, f.Mode())
} else {
f, err := os.OpenFile(
path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, rc)
if err != nil {
return err
}
}
}
return nil
}
答案 0 :(得分:37)
如果OP目录不存在,OP的解决方案稍微修改以创建包含目录dest
,并将文件提取/写入包装在一个闭包中以消除{{1每个@Nick Craig-Wood的评论调用:
defer .Close()
注意:已更新以包含Close()错误处理(如果我们正在寻找最佳做法,也可以按照所有这些做法)。
答案 1 :(得分:6)
我正在使用archive/zip
包来读取.zip文件并复制到本地磁盘。下面是根据自己的需要解压缩.zip文件的源代码。
import (
"archive/zip"
"io"
"log"
"os"
"path/filepath"
"strings"
)
func unzip(src, dest string) error {
r, err := zip.OpenReader(src)
if err != nil {
return err
}
defer r.Close()
for _, f := range r.File {
rc, err := f.Open()
if err != nil {
return err
}
defer rc.Close()
fpath := filepath.Join(dest, f.Name)
if f.FileInfo().IsDir() {
os.MkdirAll(fpath, f.Mode())
} else {
var fdir string
if lastIndex := strings.LastIndex(fpath,string(os.PathSeparator)); lastIndex > -1 {
fdir = fpath[:lastIndex]
}
err = os.MkdirAll(fdir, f.Mode())
if err != nil {
log.Fatal(err)
return err
}
f, err := os.OpenFile(
fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, rc)
if err != nil {
return err
}
}
}
return nil
}
答案 2 :(得分:3)
我一直在浏览谷歌并反复发现有人说没有可以处理的库。也许我在我的搜索中错过了一个自定义存储库,而其他人会为我们找到它。
您可以使用io.Copy(src, dest)
来简化流程,但我根本没有对其进行测试。
例如:
os.MkDirAll(dest, r.File.Mode)
d, _ := os.Open(dest)
io.Copy(r.File, d)
老实说,你的代码看起来很漂亮,如果我自己做一个提取功能(以上不起作用)那么我可能会从你的书中拿一页。
答案 3 :(得分:3)
我更喜欢和Go一起使用7zip,这会给你这样的东西。
func extractZip() {
fmt.Println("extracting", zip_path)
commandString := fmt.Sprintf(`7za e %s %s`, zip_path, dest_path)
commandSlice := strings.Fields(commandString)
fmt.Println(commandString)
c := exec.Command(commandSlice[0], commandSlice[1:]...)
e := c.Run()
checkError(e)
}
但是,如果无法使用7zip,请尝试此操作。推迟恢复以赶上恐慌。 (Example)
func checkError(e error){
if e != nil {
panic(e)
}
}
func cloneZipItem(f *zip.File, dest string){
// Create full directory path
path := filepath.Join(dest, f.Name)
fmt.Println("Creating", path)
err := os.MkdirAll(filepath.Dir(path), os.ModeDir|os.ModePerm)
checkError(err)
// Clone if item is a file
rc, err := f.Open()
checkError(err)
if !f.FileInfo().IsDir() {
// Use os.Create() since Zip don't store file permissions.
fileCopy, err := os.Create(path)
checkError(err)
_, err = io.Copy(fileCopy, rc)
fileCopy.Close()
checkError(err)
}
rc.Close()
}
func Extract(zip_path, dest string) {
r, err := zip.OpenReader(zip_path)
checkError(err)
defer r.Close()
for _, f := range r.File {
cloneZipItem(f, dest)
}
}
答案 4 :(得分:2)
在LGTM.com(我是一名开发人员)上处理Go上的ZipSlip漏洞的查询时,我注意到在一些项目中代码类似于公认的答案,例如rclone。
正如@woogoo所指出的,此代码易受ZipSlip攻击,因此我认为答案应更新为以下内容(代码来自rclone fix):
func Unzip(src, dest string) error {
dest = filepath.Clean(dest) + string(os.PathSeparator)
r, err := zip.OpenReader(src)
if err != nil {
return err
}
defer func() {
if err := r.Close(); err != nil {
panic(err)
}
}()
os.MkdirAll(dest, 0755)
// Closure to address file descriptors issue with all the deferred .Close() methods
extractAndWriteFile := func(f *zip.File) error {
path := filepath.Join(dest, f.Name)
// Check for ZipSlip: https://snyk.io/research/zip-slip-vulnerability
if !strings.HasPrefix(path, dest) {
return fmt.Errorf("%s: illegal file path", path)
}
rc, err := f.Open()
if err != nil {
return err
}
defer func() {
if err := rc.Close(); err != nil {
panic(err)
}
}()
if f.FileInfo().IsDir() {
os.MkdirAll(path, f.Mode())
} else {
os.MkdirAll(filepath.Dir(path), f.Mode())
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return err
}
defer func() {
if err := f.Close(); err != nil {
panic(err)
}
}()
_, err = io.Copy(f, rc)
if err != nil {
return err
}
}
return nil
}
for _, f := range r.File {
err := extractAndWriteFile(f)
if err != nil {
return err
}
}
return nil
}
答案 5 :(得分:2)
这里的大多数答案都是错误的,因为他们假设 Zip 存档会发出 目录条目。 Zip 档案是 not required to emit directory entries,所以给定一个这样的档案,一个简单的程序会崩溃 遇到第一个非根文件时,因为永远不会有目录 创建,并且文件创建将失败,因为目录尚未创建 然而。这是一种不同的方法:
package main
import (
"archive/zip"
"os"
"path"
)
func unzip(source, dest string) error {
read, err := zip.OpenReader(source)
if err != nil { return err }
defer read.Close()
for _, file := range read.File {
if file.Mode().IsDir() { continue }
open, err := file.Open()
if err != nil { return err }
name := path.Join(dest, file.Name)
os.MkdirAll(path.Dir(name), os.ModeDir)
create, err := os.Create(name)
if err != nil { return err }
defer create.Close()
create.ReadFrom(open)
}
return nil
}
func main() {
unzip("Microsoft.VisualCpp.Tools.HostX64.TargetX64.vsix", "tools")
}
答案 6 :(得分:0)
@Astockwell的代码中有Zip Slip Vulnerability
,请参阅:https://snyk.io/research/zip-slip-vulnerability
答案 7 :(得分:0)
package main
import (
"os"
"io"
"io/ioutil"
"fmt"
"strings"
"archive/zip"
"path/filepath"
)
func main() {
if err := foo("test.zip"); err != nil {
fmt.Println(err)
}
}
func foo(yourZipFile string) error {
tmpDir, err := ioutil.TempDir("/tmp", "yourPrefix-")
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)
if filenames, err := Unzip(yourZipFile, tmpDir); err != nil {
return err
} else {
for f := range filenames {
fmt.Println(filenames[f])
}
}
return nil
}
func Unzip(src string, dst string) ([]string, error) {
var filenames []string
r, err := zip.OpenReader(src)
if err != nil {
return nil, err
}
defer r.Close()
for f := range r.File {
dstpath := filepath.Join(dst, r.File[f].Name)
if !strings.HasPrefix(dstpath, filepath.Clean(dst) + string(os.PathSeparator)) {
return nil, fmt.Errorf("%s: illegal file path", src)
}
if r.File[f].FileInfo().IsDir() {
if err := os.MkdirAll(dstpath, os.ModePerm); err != nil {
return nil, err
}
} else {
if rc, err := r.File[f].Open(); err != nil {
return nil, err
} else {
defer rc.Close()
if of, err := os.OpenFile(dstpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, r.File[f].Mode()); err != nil {
return nil, err
} else {
defer of.Close()
if _, err = io.Copy(of, rc); err != nil {
return nil, err
} else {
of.Close()
rc.Close()
filenames = append(filenames, dstpath)
}
}
}
}
}
if len(filenames) == 0 {
return nil, fmt.Errorf("zip file is empty")
}
return filenames, nil
}