Простой способ распаковать файл с помощью golang
Есть ли простой способ распаковать файл с 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
}
Ответы
Ответ 1
Незначительный передел решения 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() (если мы ищем лучшие практики, возможно также следовать всем их).
Ответ 2
Я использую пакет 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
}
Ответ 3
Я бы предпочел использовать 7zip с Go, что даст вам что-то вроде этого.
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)
}
}
Ответ 4
Я занимаюсь просмотром google и неоднократно обнаруживал, что люди говорят, что нет библиотеки, которая может справиться с этим. Возможно, я пропустил собственный репозиторий в моем поиске, хотя и кто-то еще найдет его для нас.
Возможно, вы сможете использовать io.Copy(src, dest)
для облегчения процесса, но я его вообще не тестировал.
Например:
os.MkDirAll(dest, r.File.Mode)
d, _ := os.Open(dest)
io.Copy(r.File, d)
Честно говоря, мой код выглядит довольно красиво, и если я сам буду выполнять функцию Extract (и выше это не работает), я, вероятно, возьму страницу из вашей книги.
Ответ 5
Работая над запросом на обнаружение уязвимостей ZipSlip в Go на LGTM.com (разработчиком которого я являюсь), я заметил код, похожий на принятый ответ в нескольких проектах, например, 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
}
Ответ 6
в коде @Astockwell есть Zip Slip Vulnerability
, см.: https://snyk.io/research/zip-slip-vulnerability