3 回答

TA貢獻1794條經(jīng)驗 獲得超7個贊
下面是一些示例代碼:
package main
import (
"archive/tar"
"compress/gzip"
"io"
"os"
"path"
)
func extract(source string) error {
file, err := os.Open(source)
if err != nil { return err }
defer file.Close()
gzRead, err := gzip.NewReader(file)
if err != nil { return err }
defer gzRead.Close()
tarRead := tar.NewReader(gzRead)
for {
cur, err := tarRead.Next()
if err == io.EOF { break } else if err != nil { return err }
os.MkdirAll(path.Dir(cur.Name), os.ModeDir)
switch cur.Typeflag {
case tar.TypeReg:
create, err := os.Create(cur.Name)
if err != nil { return err }
defer create.Close()
create.ReadFrom(tarRead)
case tar.TypeLink:
os.Link(cur.Linkname, cur.Name)
}
}
return nil
}
用法:
package main
func main() {
extract("clamav-0.103.1.tar.gz")
}

TA貢獻1780條經(jīng)驗 獲得超4個贊
您可能會遇到每個進程允許的打開文件數(shù)。使用標志運行,我認為默認限制為1024。壓縮包有 2758 個文件。ulimitulimit-aopen files
這是因為在處理 .tarReader
要解決此問題,請在處理它們時關(guān)閉每個文件:
func UnTar(tarball, target string) error {
reader, err := os.Open(tarball)
if err != nil {
return err
}
defer reader.Close()
tarReader := tar.NewReader(reader)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
} else if err != nil {
return err
}
path := filepath.Join(target, header.Name)
info := header.FileInfo()
if info.IsDir() {
if err = os.MkdirAll(path, info.Mode()); err != nil {
return err
}
continue
}
err = processOneFile(tarReader, path, info.Mode())
if err != nil {
return err
}
}
return nil
}
func processOneFile(tarReader io.Reader, filePath string, fileMode os.FileMode) error {
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fileMode)
if err != nil {
return err
}
defer file.Close() // close error discarded
_, err = io.Copy(file, tarReader)
return err
}

TA貢獻1872條經(jīng)驗 獲得超4個贊
雖然關(guān)于的另一個答案已經(jīng)非常好,但我只想補充兩件事:ulimit
您可以解壓縮gzip并同時讀取tar文件,而不是在兩者之間創(chuàng)建臨時文件。您還可以直接從URL流式傳輸文件,并在下載時將其提取
有人可能會創(chuàng)建一個惡意的tar.gz文件,該文件會使您的代碼使用zipslip之類的東西覆蓋重要文件(特別是如果您的程序以root身份運行,有人可以注入一個文件路徑,從而覆蓋該文件,甚至可能編輯crontab文件并以這種方式執(zhí)行代碼?),您應(yīng)該檢查一下
../../../../etc/passwd
考慮到這一點,我們可以編寫一個直接從中提取的函數(shù),該函數(shù)還檢查目標目錄之外的任何路徑:io.Reader
// untargz decompresses a gzipped tar stream to the directory specified by target.
// Note that `file` should be closed by the caller
func untargz(file io.Reader, targetDir string) (err error) {
gz, err := gzip.NewReader(file)
if err != nil {
return
}
// This does not close file
defer gz.Close()
tarReader := tar.NewReader(gz)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
} else if err != nil {
return err
}
// This can be dangerous, similar to zipslip
path := filepath.Join(targetDir, header.Name)
// Check for ZipSlip. More Info: https://snyk.io/research/zip-slip-vulnerability#go
if !strings.HasPrefix(path, filepath.Clean(targetDir)+string(os.PathSeparator)) {
err = fmt.Errorf("%s: illegal file path", path)
return err
}
info := header.FileInfo()
if info.IsDir() {
if err = os.MkdirAll(path, info.Mode()); err != nil {
return err
}
continue
}
file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
if err != nil {
return err
}
_, err = io.Copy(file, tarReader)
if err != nil {
file.Close()
return err
}
err = file.Close()
if err != nil {
return err
}
}
return nil
}
考慮一下如果在目錄中的文件之后聲明目錄會發(fā)生什么(因為隨后會失?。部赡軙兴鶐椭?,但此函數(shù)不處理這種情況。os.Create
這個函數(shù)可以用來直接流式傳輸?shù)捷敵瞿夸洠蠈嵳f,我不確定這是否是你想要的:
func main() {
resp, err := http.Get(`https://www.clamav.net/downloads/production/clamav-0.103.1.tar.gz`)
if err != nil {
panic(err)
}
defer resp.Body.Close()
err = untargz(bufio.NewReader(resp.Body), "out")
if err != nil {
panic(err)
}
println("Done")
}
您可以在此處找到完整文件。
- 3 回答
- 0 關(guān)注
- 202 瀏覽
添加回答
舉報