2014-04-09 13 views
6

Sto scrivendo un server web in go.http Request.FormFile: gestisce i file zip?

Su una delle pagine, l'utente può caricare un file.

Mi piacerebbe essere in grado di gestire i file zip.

Nel pacchetto archive/zip, vedo solo due funzioni che mi permettono di leggere da un archivio zip:

  1. func OpenReader(name string) (*ReadCloser, error)
  2. func NewReader(r io.ReaderAt, size int64) (*Reader, error)

vorrei evitare di scrivere e leggere di nuovo dal disco,
se si desidera utilizzare la seconda funzione, è necessario conoscere la dimensione del file caricato prima di chiamare la funzione.

Domanda

Ho diviso la mia domanda in due parti:

  1. Quale sarebbe il modo idiomatico per leggere il contenuto decompresso di un file zip caricato attraverso una forma multipart/form-data standard HTML?

  2. Come posso ottenere la dimensione effettiva di un file caricato tramite un modulo html?

    func(req *http.Request) { 
        f, h, err := req.FormFile("fileTag") 
        if err != nil { 
         panic(err) 
        } 
        var fileSize int = ?? 
    
        unzipper, err := zip.NewReader(f, fileSize) 
    } 
    

risposta

2

Ecco un modo che ho trovato per ottenere la dimensione:

func(req *http.Request) { 
    f, h, err := req.FormFile("fileTag") 
    if err != nil { 
     panic(err) 
    } 
    fileSize, err := f.Seek(0, 2) //2 = from end 
    if err != nil { 
     panic(err) 
    } 
    _, err = f.Seek(0, 0) 
    if err != nil { 
     panic(err) 
    } 

    unzipper, err := zip.NewReader(f, fileSize) 
} 

I non trovi questa soluzione molto elegante o idiomatica.

Non c'è un modo più semplice per gestire questo caso?

+0

la soluzione mime-header della mia risposta non funziona? Se ti manca una lunghezza del contenuto, questa soluzione è in realtà molto buona, dal momento che non penso che ci sia un modo per farlo entrare in un Buffer senza almeno un'altra copia. – JimB

2

È possibile cercare la dimensione del file nella FormFile del Header (che è un MIMEHEader).

h.Header.Get("Content-Length") 

Se non c'è lunghezza del contenuto per il file, si può leggere in un buffer prima per ottenere la dimensione.

Altre opzioni sono cercare fino in fondo, come si inserisce la risposta, o estrarre il lettore concreto dall'interfaccia. Un file in più parti sarà un *io.SectionReader se è in memoria, o un *os.File se è stato scritto in un file temporaneo:

switch f := f.(type) { 
case *io.SectionReader: 
    fileSize = r.Size() 
case *os.File: 
    if s, err := f.Stat(); err == nil { 
     fileSize = s.Size() 
    } 
} 
+0

No 'Content-Length' nelle intestazioni ... Tuttavia, 'Content-Length' sarebbe il valore dell'intestazione scritta dal client, giusto? – LeGEC

+0

sì, è un'intestazione nel modulo multipart (* non * le intestazioni delle richieste http), quindi dovrebbe essere inviata dal client. – JimB

+0

Grazie per il suggerimento di SectionReader. Sfortunatamente, f può essere sia un SectionReader, sia un file os.File. Vedi [il codice per FileHeader.Open] (http://golang.org/src/pkg/mime/multipart/formdata.go?s=2942:2984#L121) – LeGEC

1

vorrei utilizzare un buffer in memoria e assicurarsi di limitare la dimensione di upload massima di un file (~ 100MB?) Qui si sta usando io.Copy

import (
    "archive/zip" 
    "bytes" 
    "io" 
    "net/http" 
) 

func HttHandler(req *http.Request) { 

    f, _, err := req.FormFile("fileTag") 

    if err != nil { 
     panic(err) 
    } 

    buf := new(bytes.Buffer) 

    fileSize, err := io.Copy(buf, f) 
    if err != nil { 
     panic(err) 
    } 

    zip.NewReader(bytes.NewReader(buf.Bytes()), fileSize) 

}