2014-04-09 4 views
6

Ich schreibe einen Webserver in go.http Request.FormFile: Zip-Dateien behandeln?

Auf einer der Seiten kann der Benutzer eine Datei hochladen.

Ich würde gerne mit Zip-Dateien umgehen können.

Im archive/zip Paket sehe ich nur zwei Funktionen, die mir erlauben, von einem Zip-Archiv zu lesen:

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

Ich möchte schreiben vermeiden und das Lesen zurück von der Platte,
Wenn ich die zweite Funktion verwenden möchte, muss ich die Größe der hochgeladenen Datei vor dem Aufruf der Funktion wissen.

Frage

Ich werde meine Frage in zwei Teile geteilt:

  1. Was wäre der idiomatische Weg, um den entpackten Inhalt einer Zip-Datei über einen Standard-multipart/form-data HTML-Formular hochgeladen zu lesen?

  2. Wie kann ich die tatsächliche Größe einer über ein HTML-Formular hochgeladenen Datei erhalten?

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

Antwort

2

Hier ist eine Art, wie ich die Größe bekommen gefunden:

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 finde diese Lösung nicht sehr elegant oder idiomatisch.

Gibt es keinen saubereren Weg, diesen Fall zu behandeln?

+0

funktioniert die Mime-Header-Lösung aus meiner Antwort nicht? Wenn Sie eine Inhaltslänge vermissen, ist diese Lösung eigentlich sehr gut, da ich nicht glaube, dass es einen Weg gibt, es in einen Puffer ohne mindestens eine Kopie zu bringen. – JimB

2

Sie können in der Header der Formfile für die Dateigröße aussehen (die eine MIMEHEader ist).

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

Wenn keine Inhaltslänge für die Datei vorhanden ist, können Sie sie zuerst in einen Puffer einlesen, um die Größe zu ermitteln.

Andere Optionen sind, bis zum Ende zu suchen, wie Sie Ihre Antwort eingeben, oder den konkreten Reader aus der Schnittstelle holen. Ein mehrteiliger Datei wird ein *io.SectionReader sein, wenn es im Speicher ist, oder ein *os.File wenn es wurde in eine temporäre Datei geschrieben:

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

Keine 'Inhalt-Length' in den Headern ... "Content-Length" wäre jedoch der Wert der vom Client geschriebenen Kopfzeile, richtig? – LeGEC

+0

Ja, es ist ein Header in der Multipart-Form (* nicht * die HTTP-Request-Header), also müsste es vom Client gesendet werden. – JimB

+0

Danke für den SectionReader-Vorschlag. Leider kann f entweder ein SectionReader oder eine os.File sein. Siehe [der Code für FileHeader.Open] (http://golang.org/src/pkg/mime/multipart/formdata.go?s=2942:2984#L121) – LeGEC

1

würde ich einen speicherinternen Puffer verwenden und stellen Sie sicher, dass die maximale Upload-Größe einer Datei zu begrenzen (~ 100 MB?) Hier wird mit 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) 

}