Как проверить, является ли файл допустимым?

Я создаю веб-приложение.

На одной из страниц есть форма загрузки, где пользователь может загрузить файл. После завершения загрузки я хочу проверить сервер, если загруженный файл является изображением.

Можно ли проверить это за пределами простой проверки расширения файлов (т.е. не предполагая, что имя файла *.png на самом деле является PNG-изображением)?

Например, если я редактирую изображение JPEG, добавляя/редактируя байт в случайном месте, чтобы сделать недопустимый файл JPEG, я хочу обнаружить, что он больше не является изображением JPEG. Некоторое время назад я использовал такие вещи через PHP, используя библиотеку GD.

Я хотел бы знать, возможно ли с Go?

Ответ 1

Обычно делается проверка того, имеет ли файл правильный магический номер для нужного формата файла изображения. Хотя этот тест не является очень точным, он обычно достаточно хорош. Вы можете использовать такой код:

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 ""
}

Ответ 2

Пакет http может сделать это для вас:

func DetectContentType(data []byte) string

DetectContentType реализует алгоритм, описанный в http://mimesniff.spec.whatwg.org/, чтобы определить Content-Type данные. Он считает не более первых 512 байт данных. Функция DetectContentType всегда возвращает допустимый тип MIME: если он не может определить более конкретный, он возвращает "application/octet-stream".

Код: https://golang.org/src/net/http/sniff.go

Ответ 3

DetectContentType лучше, чем проверка магического числа вручную. Простое использование:

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.

Используя этот метод, вы должны знать, что у вас все еще не гарантируется наличие правильного файла. Поэтому я бы рекомендовал сделать некоторые манипуляции с этим файлом (например, изменить его размер, чтобы убедиться, что это действительно изображение).