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
}
OPのソリューションを少し手直しして、含まれているディレクトリdest
が存在しない場合は作成し、ファイルの抽出/書き込みをクロージャーにラップして、defer .Close()
呼び出しのスタックを排除します- @ Nick Craig-Wood のコメント:
func Unzip(src, dest string) error {
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 {
rc, err := f.Open()
if err != nil {
return err
}
defer func() {
if err := rc.Close(); err != nil {
panic(err)
}
}()
path := filepath.Join(dest, f.Name)
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
}
注:Close()エラー処理も含めるように更新されました(ベストプラクティスを探している場合は、それらのすべてに従うこともできます)。
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
}
私はグーグルのブラウジングを何回か行っており、それを処理できるライブラリはないと言っている人々を繰り返し見つけました。多分私は私の検索でカスタムリポジトリを逃しました、そして誰かが私たちのためにそれを見つけるでしょう。
プロセスを簡単にするためにio.Copy(src, dest)
を利用できるかもしれませんが、私はまったくテストしていません。
例えば:
os.MkDirAll(dest, r.File.Mode)
d, _ := os.Open(dest)
io.Copy(r.File, d)
正直に言って、あなたのコードはかなり見栄えがよく、もし私が自分でExtract関数を実行した場合(そして上記が機能しない場合)、おそらくあなたの本からページを取得するでしょう。
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を使用できない場合は、これを試してください。パニックに対処するために回復を延期します。 ( 例 )
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)
}
}
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
}
有る Zip Slip Vulnerability
@Astockwellのコードで、以下を参照してください: https://snyk.io/research/Zip-slip-vulnerability