Saltar al contenido

manera fácil de descomprimir archivos con golang

Te sugerimos que revises esta solución en un ambiente controlado antes de enviarlo a producción, un saludo.

Solución:

Ligera reelaboración de la solución del OP para crear el directorio contenedor dest si no existe, y envolver la extracción/escritura del archivo en un cierre para eliminar el apilamiento de defer .Close() llamadas según el comentario de @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)

        // Check for ZipSlip (Directory traversal)
        if !strings.HasPrefix(path, filepath.Clean(dest) + string(os.PathSeparator)) 
            return fmt.Errorf("illegal file path: %s", path)
        

        if f.FileInfo().IsDir() 
            os.MkdirAll(path, f.Mode())
         else os.O_CREATE
        return nil
    

    for _, f := range r.File 
        err := extractAndWriteFile(f)
        if err != nil 
            return err
        
    

    return nil

Nota: Actualizado para incluir también el manejo de errores Close() (si estamos buscando las mejores prácticas, también podemos seguirlas TODAS).

Estoy usando archive/zip paquete para leer archivos .zip y copiarlos en el disco local. A continuación se muestra el código fuente para descomprimir archivos .zip para mis propias necesidades.

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
    
    return nil

Preferiría usar 7zip con Go, que te daría algo como esto.

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)

Mejor código de ejemplo

Sin embargo, si no es posible usar 7zip, intente esto. Aplazar una recuperación para atrapar los pánicos. (Ejemplo)

func checkError(e error)
  if e != nil 
    panic(e)
  

func cloneZipItem(f *zip.File, dest string)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)
    

Eres capaz de añadir valor a nuestra información asistiendo con tu veteranía en las críticas.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *