2014-04-09 12 views
6

Piszę serwer WWW w podróży.http Request.FormFile: obsługi plików zip?

Na jednej ze stron użytkownik może przesłać plik.

Chciałbym móc obsługiwać pliki zip.

W pakiecie archive/zip, widzę tylko dwie funkcje, które pozwalają mi czytać z archiwum zip:

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

chciałabym uniknąć pisania i czytania z powrotem z dysku,
jeśli chcę użyć drugiej funkcji, muszę znać rozmiar przesłanego pliku przed wywołaniem funkcji.

Pytanie

będę podzielić na moje pytanie w dwóch częściach:

  1. Jaki byłby idiomatyczne sposób odczytać rozpakowany zawartość pliku zip przesłanego za pośrednictwem standardowego formularza multipart/form-data html?

  2. Jak mogę uzyskać rzeczywisty rozmiar pliku przesłanego przez formularz 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) 
    } 
    

Odpowiedz

2

Oto sposób znalazłem, aby uzyskać rozmiar:

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 nie uważam tego rozwiązania za bardzo eleganckie lub idiomatyczne.

Czy nie ma jakiegoś czystszego sposobu obsługi tego przypadku?

+0

czy rozwiązanie nagłówka mime z mojej odpowiedzi nie działa? Jeśli brakuje ci długości treści, to rozwiązanie jest bardzo dobre, ponieważ nie sądzę, że istnieje sposób na przeniesienie go do bufora bez przynajmniej jednej kopii. – JimB

2

Możesz zajrzeć na rozmiar pliku w FormFile na Header (co jest MIMEHEader).

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

Jeśli długość pliku nie istnieje, można go najpierw odczytać w buforze, aby uzyskać rozmiar.

var buff bytes.Buffer 
fileSize, err := buff.ReadFrom(f) 

Inne opcje to dotarcie do końca, gdy umieścisz swoją odpowiedź, lub wyjęcie konkretnego czytnika z interfejsu. Wieloczęściowy Plik będzie *io.SectionReader czy to w pamięci, albo *os.File jeśli został zapisany do pliku temp:

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' w nagłówkach ... Jednak "Content-Length" będzie wartością nagłówka napisaną przez klienta, prawda? – LeGEC

+0

tak, jest to nagłówek w postaci wieloczęściowej (* nie * nagłówki żądań http), więc musiałby zostać wysłany przez klienta. – JimB

+0

Dzięki za sugestię SectionReader. Niestety, f może być albo SectionReader, albo os.File. Zobacz [kod FileHeader.Open] (http://golang.org/src/pkg/mime/multipart/formdata.go?s=2942:2984#L121) – LeGEC

1

użyłbym bufor w pamięci i upewnij się, aby ograniczyć maksymalny rozmiar wysyłania pliku (~ 100MB?) Tutaj używa 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) 

} 
Powiązane problemy