2014-09-21 5 views
12

Ich baue eine Webanwendung.Wie überprüft man, ob eine Datei ein gültiges Bild ist?

Auf einer der Seiten befindet sich ein Upload-Formular, in das der Benutzer eine Datei hochladen kann. Nachdem der Upload abgeschlossen ist, möchte ich auf dem Server überprüfen, ob die hochgeladene Datei ein Bild ist.

Ist es möglich, dies über die einfache Prüfung der Dateierweiterung hinaus zu überprüfen (d. H. Nicht davon auszugehen, dass ein *.png Dateiname tatsächlich ein PNG-Bild ist)?

Wenn ich zum Beispiel ein JPEG-Bild editiere, das ein Byte an einer zufälligen Stelle hinzufügt/bearbeitet, um eine ungültige JPEG-Datei zu erstellen, möchte ich feststellen, dass es kein JPEG-Bild mehr ist. Ich habe diese Art von Dingen vor einiger Zeit über PHP mit einer GD-Bibliothek gemacht.

Ich würde gerne wissen, ob es möglich ist, mit Go zu tun?

+2

Was Sie wollen, ist nicht immer möglich. Wenn Sie ein zufälliges Zeichen in die Mitte einer Bilddatei einfügen, wird das Bild beschädigt, aber es wird normalerweise nicht aufhören, ein Bild zu sein. – fuz

Antwort

10

In der Regel wird geprüft, ob die Datei das richtige magic number für das gewünschte Bilddateiformat hat. Während dieser Test nicht sehr genau ist, ist es normalerweise gut genug. Sie können Code verwenden:

package foo 

import "strings" 

// image formats and magic numbers 
var magicTable = map[string]string{ 
    "\xff\xd8\xff":  "image/jpeg", 
    "\x89PNG\r\n\x1a\n": "image/png", 
    "GIF87a":   "image/gif", 
    "GIF89a":   "image/gif", 
} 

// mimeFromIncipit returns the mime type of an image file from its first few 
// bytes or the empty string if the file does not look like a known file type 
func mimeFromIncipit(incipit []byte) string { 
    incipitStr := []byte(incipit) 
    for magic, mime := range magicTable { 
     if strings.HasPrefix(incipitStr, magic) { 
      return mime 
     } 
    } 

    return "" 
} 
+0

Wenn die Überprüfung der Kopfzeile aus irgendeinem Grund nicht ausreicht, können Sie alle relevanten Bildpakete ('image/png',' image/gif', 'image/jpeg',' golang.org/x/image/bmp') laden usw.) und verwende 'image.DecodeConfig' oder [' image.Decode'] (https://golang.org/pkg/image/#Decode) und überprüfe auf Fehler. Vorsicht vor bösartigen Daten, die auf Fehler in den Decodern abzielen. –

+0

@DaveC Wie in meinem eigenen Kommentar weiter oben gesagt, löst das Entschlüsseln des Bildes das Problem nicht, da das Ändern zufälliger Bytes in der Mitte des Bildes im Allgemeinen nicht dazu führt, dass die Bilddatei als "beschädigt" erkannt wird. – fuz

+3

in der Tat! Ohne einen sicheren Hash des Originals können Sie keine gutartigen Änderungen feststellen. Jedoch * wenn gewünscht * (wie Ihre Antwort sagt, nur die Kopfzeile ist in der Regel gut genug) können Sie überprüfen, ob die vollständigen Daten ein gültiges Bild darstellen (vorausgesetzt, die relevante Bilddekodierung meldet Fehler für schlechte/ungültige Eingabe und nicht nur eine beschädigte Datei) Bild ohne Fehler, zB einfache Bildformate, die nur die Rohpixeldaten enthalten, werden gut dekodiert, solange die korrekte Anzahl von Bytes vorhanden ist, während ich hoffe, dass etwas wie ein JPEG-Decoder wählerischer ist wegen seiner komprimierten Eingabe). –

9

Das http-Paket kann dies für Sie tun:

func DetectContentType(data []byte) string 

DetectContentType den Algorithmus bei http://mimesniff.spec.whatwg.org/ beschrieben implementiert das Content-Type des zu bestimmen gegeben Daten. Es berücksichtigt höchstens die ersten 512 Datenbytes. DetectContentType gibt immer einen gültigen MIME-Typ zurück: Wenn es nicht bestimmen kann, gibt es "application/octet-stream" zurück.

Code: https://golang.org/src/net/http/sniff.go

+0

Großartig! Vielen Dank. – Deleplace

9

DetectContentType ist viel besser als eine manuelle magische Zahl zu überprüfen. Die Verwendung ist einfach:

clientFile, _, _ := r.FormFile("img") // or get your file from a file system 
defer clientFile.Close() 
buff := make([]byte, 512) // docs tell that it take only first 512 bytes into consideration 
if _, err = clientFile.Read(buff); err != nil { 
    fmt.Println(err) // do something with that error 
    return 
} 

fmt.Println(http.DetectContentType(buff)) // do something based on your detection. 

Mit dieser Methode müssen Sie wissen, dass Sie immer noch nicht über eine korrekte Datei verfügen. Ich würde daher empfehlen, mit dieser Datei eine Bildbearbeitung durchzuführen (z. B. die Größe zu ändern, um sicherzustellen, dass es sich wirklich um ein Bild handelt).