existe-t-il un moyen facile de décompresser un fichier avec golang?
en ce moment mon code est:
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
}
Légère retouche de la solution de l'OP pour créer le répertoire contenant dest
s'il n'existe pas, et pour encapsuler l'extraction/écriture du fichier dans une fermeture pour éliminer l'empilement des appels de defer .Close()
par - @ Nick Craig-Wood commentaire:
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
}
Remarque: Mise à jour pour inclure également la gestion des erreurs Close () (si nous recherchons les meilleures pratiques, elles peuvent toutes les suivre).
J'utilise archive/Zip
package pour lire les fichiers .zip et copier sur le disque local. Voici le code source pour décompresser les fichiers .zip pour mes propres besoins.
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
}
J'ai fait quelques recherches sur Google et j'ai trouvé à plusieurs reprises des gens disant qu'il n'y avait pas de bibliothèque capable de gérer cela. Peut-être que j'ai raté un référentiel personnalisé dans ma recherche et que quelqu'un d'autre le trouvera pour nous.
Vous pourrez peut-être utiliser io.Copy(src, dest)
pour faciliter le processus mais je ne l'ai pas testé du tout.
Par exemple:
os.MkDirAll(dest, r.File.Mode)
d, _ := os.Open(dest)
io.Copy(r.File, d)
Honnêtement, pour moi, votre code est plutôt joli et si je devais faire moi-même une fonction d'extraction (et ce qui précède ne fonctionne pas), je prendrais probablement une page de votre livre.
Je préférerais utiliser 7Zip avec Go, ce qui vous donnerait quelque chose comme ça.
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)
}
Mieux exemple de code
Cependant, si l'utilisation de 7Zip n'est pas possible, essayez ceci. Reportez une reprise pour attraper les paniques. ( exemple )
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
}
il y a Zip Slip Vulnerability
dans le code de @Astockwell, voir: https://snyk.io/research/Zip-slip-vulnerability