【问题标题】:How to check if a file is a valid image?如何检查文件是否为有效图像?
【发布时间】:2014-11-15 13:14:36
【问题描述】:

我正在构建一个 Web 应用程序。

在其中一个页面上有一个上传表单,用户可以在其中上传文件。上传完成后,我想在服务器上检查上传的文件是不是图片。

除了简单的文件扩展名检查(即不假设 *.png 文件名实际上是 PNG 图像)之外,是否可以检查这一点?

例如,如果我编辑 JPEG 图像,在随机位置添加/编辑一个字节以制作无效的 JPEG 文件,我想检测它不再是 JPEG 图像。前段时间我曾经使用 GD 库通过 PHP 做这种类型的事情。

我想知道是否可以用 Go 做?

【问题讨论】:

  • 你想要的并不总是可能的。将随机字符插入图像文件的中间会损坏图像,但通常不会停止成为图像。

标签: go mime


【解决方案1】:

通常要做的是检查文件是否具有您想要的图像文件格式的正确magic number。虽然这个测试不是非常准确,但通常已经足够好了。你可以使用这样的代码:

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

【讨论】:

  • 如果由于某种原因检查标题还不够,您可以加载所有相关图像包(image/pngimage/gifimage/jpeggolang.org/x/image/bmp 等)并使用@987654328 @ 或 image.Decode 并检查错误。但请注意解码器中针对恶意数据的错误。
  • @DaveC 正如我自己在上面的评论中所说,解码图像并不能解决问题,因为更改图像中间的随机字节通常不会导致图像文件被识别为“损坏。”
  • 确实!如果没有原始的安全哈希,您将无法检测到良性修改。但是,如果需要(正如您的回答所说,通常只有标题就足够了)您可以检查完整数据是否代表有效图像(假设相关图像解码报告错误/无效输入而不是不仅仅是返回没有错误的损坏图像;例如,只要存在正确的字节数,仅包含原始像素数据的简单图像格式就可以很好地解码,而我希望像 JPEG 解码器这样的东西对它的压缩更加挑剔输入)。
【解决方案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.
    

    使用这种方法你需要知道你仍然不能保证有一个正确的文件。所以我建议对该文件进行一些图像处理(比如调整它的大小以确保这确实是一张图像)。

    【讨论】:

    • 您能否进一步解释一下。所以我们调整大小,然后我们还应该做什么?
    猜你喜欢
    • 2010-10-27
    • 1970-01-01
    • 2017-08-31
    • 2011-01-12
    • 2020-12-04
    • 2015-09-30
    • 2013-08-15
    相关资源
    最近更新 更多