ios 从 NSData 或 UIImage 中查找图像类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4147311/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 18:03:56  来源:igfitidea点击:

Finding image type from NSData or UIImage

iphoneiosuiimagensdatapersist

提问by pschang

I am loading an image from a URL provided by a third-party. There is no file extension (or filename for that matter) on the URL (as it is an obscured URL). I can take the data from this (in the form of NSData) and load it into a UIImage and display it fine.

我正在从第三方提供的 URL 加载图像。URL 上没有文件扩展名(或文件名)(因为它是一个模糊的 URL)。我可以从中获取数据(以 NSData 的形式)并将其加载到 UIImage 中并正常显示。

I want to persist this data to a file. However, I don't know what format the data is in (PNG, JPG, BMP)? I assume it is JPG (since it's an image from the web) but is there a programmatic way of finding out for sure? I've looked around StackOverflow and at the documentation and haven't been able to find anything.

我想将此数据保存到文件中。但是,我不知道数据是什么格式(PNG、JPG、BMP)?我假设它是 JPG(因为它是来自网络的图像)但是有没有一种程序化的方式来确定?我环顾了 StackOverflow 和文档,但没有找到任何东西。

TIA.

TIA。



Edit: Do I really need the file extension? I'm persisting it to an external storage (Amazon S3) but considering that it will always be used in the context of iOS or a browser (both of whom seem fine in interpreting the data without an extension) perhaps this is a non-issue.

编辑:我真的需要文件扩展名吗?我将它持久化到外部存储(Amazon S3),但考虑到它将始终在 iOS 或浏览器的上下文中使用(两者似乎都可以在没有扩展的情况下解释数据)也许这不是问题.

回答by wl.

If you have NSData for the image file, then you can guess at the content type by looking at the first byte:

如果您有图像文件的 NSData,那么您可以通过查看第一个字节来猜测内容类型:

+ (NSString *)contentTypeForImageData:(NSData *)data {
    uint8_t c;
    [data getBytes:&c length:1];

    switch (c) {
    case 0xFF:
        return @"image/jpeg";
    case 0x89:
        return @"image/png";
    case 0x47:
        return @"image/gif";
    case 0x49:
    case 0x4D:
        return @"image/tiff";
    }
    return nil;
}

回答by akashivskyy

Improving upon wl.'s answer, here's a much more extended and precise way to predict the image's MIME type based on the signature. The code was largely inspired by php's ext/standard/image.c.

改进wl.'s answer,这里有一种更扩展和更精确的方法来根据签名预测图像的 MIME 类型。代码很大程度上受到了 php 的ext/standard/image.c 的启发

- (NSString *)mimeTypeByGuessingFromData:(NSData *)data {

    char bytes[12] = {0};
    [data getBytes:&bytes length:12];

    const char bmp[2] = {'B', 'M'};
    const char gif[3] = {'G', 'I', 'F'};
    const char swf[3] = {'F', 'W', 'S'};
    const char swc[3] = {'C', 'W', 'S'};
    const char jpg[3] = {0xff, 0xd8, 0xff};
    const char psd[4] = {'8', 'B', 'P', 'S'};
    const char iff[4] = {'F', 'O', 'R', 'M'};
    const char webp[4] = {'R', 'I', 'F', 'F'};
    const char ico[4] = {0x00, 0x00, 0x01, 0x00};
    const char tif_ii[4] = {'I','I', 0x2A, 0x00};
    const char tif_mm[4] = {'M','M', 0x00, 0x2A};
    const char png[8] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
    const char jp2[12] = {0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a};


    if (!memcmp(bytes, bmp, 2)) {
        return @"image/x-ms-bmp";
    } else if (!memcmp(bytes, gif, 3)) {
        return @"image/gif";
    } else if (!memcmp(bytes, jpg, 3)) {
        return @"image/jpeg";
    } else if (!memcmp(bytes, psd, 4)) {
        return @"image/psd";
    } else if (!memcmp(bytes, iff, 4)) {
        return @"image/iff";
    } else if (!memcmp(bytes, webp, 4)) {
        return @"image/webp";
    } else if (!memcmp(bytes, ico, 4)) {
        return @"image/vnd.microsoft.icon";
    } else if (!memcmp(bytes, tif_ii, 4) || !memcmp(bytes, tif_mm, 4)) {
        return @"image/tiff";
    } else if (!memcmp(bytes, png, 8)) {
        return @"image/png";
    } else if (!memcmp(bytes, jp2, 12)) {
        return @"image/jp2";
    }

    return @"application/octet-stream"; // default type

}

The above method recognizes the following image types:

上述方法识别以下图像类型:

  • image/x-ms-bmp(bmp)
  • image/gif(gif)
  • image/jpeg(jpg, jpeg)
  • image/psd(psd)
  • image/iff(iff)
  • image/webp(webp)
  • image/vnd.microsoft.icon(ico)
  • image/tiff(tif, tiff)
  • image/png(png)
  • image/jp2(jp2)
  • image/x-ms-bmp(bmp)
  • image/gif(gif)
  • image/jpeg(jpg, jpeg)
  • image/psd(psd)
  • image/iff(如果)
  • image/webp(网络)
  • image/vnd.microsoft.icon(ico)
  • image/tiff(tif, tiff)
  • image/png(png)
  • image/jp2(jp2)

Unfortunately, there is no simple way to get this kind of information from a UIImageinstance, because its encapsulated bitmap data cannot be accessed.

不幸的是,没有简单的方法可以从UIImage实例中获取此类信息,因为无法访问其封装的位图数据。

回答by ccoroom

@Tai Le's solution for Swift 3 is assigning whole data into byte array. If an image large, it can cause crash. This solution just assigns single byte:

@Tai Le 的 Swift 3 解决方案是将整个数据分配到字节数组中。如果图像很大,可能会导致崩溃。此解决方案仅分配单个字节:

import Foundation

public extension Data {
    var fileExtension: String {
        var values = [UInt8](repeating:0, count:1)
        self.copyBytes(to: &values, count: 1)

        let ext: String
        switch (values[0]) {
        case 0xFF:
            ext = ".jpg"
        case 0x89:
            ext = ".png"
        case 0x47:
            ext = ".gif"
        case 0x49, 0x4D :
            ext = ".tiff"
        default:
            ext = ".png"
        }
        return ext
    }
}

回答by Dave DeLong

If you're retrieving the image from a URL, then presumably you can inspect the HTTP response headers. Does the Content-Typeheader contain anything useful? (I'd imagine it would since a browser would probably be able to display the image correctly, and it could only do that if the content type were appropriately set)

如果您从 URL 检索图像,那么您大概可以检查 HTTP 响应标头。Content-Type标题是否包含任何有用的内容?(我想它会,因为浏览器可能能够正确显示图像,并且只有在内容类型设置正确的情况下才能做到这一点)

回答by Tai Le

Swift3 version:

Swift3 版本:

let data: Data = UIImagePNGRepresentation(yourImage)!

extension Data {
    var format: String {
        let array = [UInt8](self)
        let ext: String
        switch (array[0]) {
        case 0xFF:
            ext = "jpg"
        case 0x89:
            ext = "png"
        case 0x47:
            ext = "gif"
        case 0x49, 0x4D :
            ext = "tiff"
        default:
            ext = "unknown"
        }
        return ext
    }
}

回答by ooOlly

An alternative of accepted answer is checking image's UTI with image I/O frameWork. You can achieve image type form UTI. try this:

接受答案的替代方法是使用image I/O frameWork. 您可以通过 UTI 实现图像类型。尝试这个:

CGImageSourceRef imgSrc = CGImageSourceCreateWithData((CFDataRef)data, NULL);
NSString *uti = (NSString*)CGImageSourceGetType(imgSrc);
NSLog(@"%@",uti);

For example, a GIF image's UTI is "com.compuserve.gif" and PNG image's UTI is "public.png".BUT you can't achieve UTI from image which image I/O frameWorkdoesn't recognized.

例如,GIF 图像的 UTI 是“com.compuserve.gif”,PNG 图像的 UTI 是“public.png”。但是您无法从image I/O frameWork无法识别的图像中获得 UTI 。

回答by Steven Fisher

If it really matters to you, I believe you'll have to examine the bytestream. A JPEG will start with the bytes FF D8. A PNG will start with 89 50 4E 47 0D 0A 1A 0A. I don't know if BMP has a similar header, but I don't think you're too likely to run into those on the web in 2010.

如果这对您来说真的很重要,我相信您必须检查字节流。JPEG 将以字节 FF D8 开头。PNG 将以 89 50 4E 47 0D 0A 1A 0A 开头。我不知道 BMP 是否有类似的标题,但我认为您在 2010 年不太可能在网络上遇到这些标题。

But does it really matter to you? Can't you just treat it as an unknown image and let Cocoa Touch do the work?

但这对你来说真的很重要吗?你不能把它当作一个未知的图像,让 Cocoa Touch 来完成工作吗?

回答by Peter Kreinz

My improved solution based on @ccoroom

我基于@ccoroom 的改进解决方案

//  Data+ImageContentType.swift

import Foundation

extension Data {  
    enum ImageContentType: String {
        case jpg, png, gif, tiff, unknown

        var fileExtension: String {
            return self.rawValue
        }
    }

    var imageContentType: ImageContentType {

        var values = [UInt8](repeating: 0, count: 1)

        self.copyBytes(to: &values, count: 1)

        switch (values[0]) {
        case 0xFF:
            return .jpg
        case 0x89:
            return .png
        case 0x47:
           return .gif
        case 0x49, 0x4D :
           return .tiff
        default:
            return .unknown
        }
    }
}

Some usage examples:

一些使用示例:

//load some image
do {
    let imageData = try Data(contentsOf: URL(string: "https://myServer/images/test.jpg")!)
} catch {
    print("Unable to load image: \(error)")
}

//content type check
guard [Data.ImageContentType.jpg,
       Data.ImageContentType.png].contains(imageData.imageContentType) else {
    print("unsupported image type")
            return
        }

//set file extension
let image = "myImage.\(imageData.imageContentType.fileExtension)" //myImage.jpg

回答by Leo Dabus

To get the image type from an UIImageyou can get the type identifier (UTI) from the underlying Quartz image data:

要从 an 获取图像类型,UIImage您可以从底层 Quartz 图像数据中获取类型标识符 (UTI):

extension UIImage {
    var typeIdentifier: String? {
        cgImage?.utType as String?
    }
}

To get the image type identifier from a URL it will depend on whether the URL points to a local resource or not:

要从 URL 获取图像类型标识符,它将取决于 URL 是否指向本地资源:

extension URL {
    // for local resources (fileURLs)
    var typeIdentifier: String? { (try? resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier }
    // for non local resources (web) you can get it asyncronously
    func asyncTypeIdentifier(completion: @escaping ((String?, Error?) -> Void)) {
        var request = URLRequest(url: self)
        request.httpMethod = "HEAD"
        URLSession.shared.dataTask(with: request) { _ , response , error in
            completion((response as? HTTPURLResponse)?.mimeType, error)
        }.resume()
    }
}


let imageURL = URL(string: "https://i.stack.imgur.com/varL9.jpg")!
imageURL.asyncTypeIdentifier { typeIdentifier, error in
    guard let typeIdentifier = typeIdentifier, error == nil else { return }
    print("typeIdentifier:", typeIdentifier)
}

回答by MoDJ

Implement a signature check for each known image format. Here is a quick Objective-C function that does that for PNG data:

对每个已知的图像格式实施签名检查。这是一个快速的 Objective-C 函数,它对 PNG 数据执行此操作:

// Verify that NSData contains PNG data by checking the signature

- (BOOL) isPNGData:(NSData*)data
{
  // Verify that the PNG file signature matches

  static const
  unsigned char   png_sign[8] = {137, 80, 78, 71, 13, 10, 26, 10};

  unsigned char   sig[8] = {0, 0, 0, 0, 0, 0, 0, 0};

  if ([data length] <= 8) {
    return FALSE;
  }

  [data getBytes:&sig length:8];

  BOOL same = (memcmp(sig, png_sign, 8) == 0);

  return same;
}