Finding image type from NSData or UIImage

45,889

Solution 1

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

+ (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;
}

Solution 2

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.

- (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)

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

Solution 3

@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:

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

Solution 4

If you're retrieving the image from a URL, then presumably you can inspect the HTTP response headers. Does the Content-Type header 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)

Solution 5

Swift3 version:

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
    }
}
Share:
45,889
pschang
Author by

pschang

Updated on July 05, 2022

Comments

  • pschang
    pschang almost 2 years

    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.

    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.

    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.

  • pschang
    pschang over 13 years
    Well I'm storing it on a third-party (Amazon S3) and eventually intend on using this image on a web site as well. Although, now that I have it working, I see that the browser knows how to render the image regardless of a file extension. And we know iOS's UIImage doesn't care. So maybe it doesn't matter?
  • pschang
    pschang over 13 years
    Oh yes. I hadn't thought of this! Good idea.
  • Steven Fisher
    Steven Fisher over 13 years
    My guess is that it doesn't matter, but if you care about IE doing the right thing to the files without extensions you should probably test it as well.
  • Devarshi
    Devarshi over 10 years
    is there any equivalent solution to identify type of files of other kinds ... such as rtf, mov, mp3?
  • Shyam Bhat
    Shyam Bhat about 10 years
    This doesn't help me in case of identifying an UIImage object. To extract NSData from an UIImage, I need to use either UIImagePNGRepresentation() or UIImageJPEGRepresentation() which converts the imageData by itself.
  • nzeltzer
    nzeltzer over 9 years
    Unfortunately, UIImage doesn't provide any access to image context – you won't be able to determine the image type without reference to the original byte stream.
  • nzeltzer
    nzeltzer over 9 years
    An alternative approach: CGImageSource (part of the ImageIO framework) can provide you with the appropriate uniform type identifier for image data.
  • rob5408
    rob5408 over 9 years
    I had a jpg with a first byte of 0xDB?
  • Arun Gupta
    Arun Gupta about 9 years
    Did anyone know bytes for pdf, word(doc, docx), excel(xls, xlsx), powerpoint(ppt, pptx), text, rtf..
  • alfonso
    alfonso over 6 years
    Great list, thanks. I'm having trouble with the new .heic image type used in iOS11. Could you update your answer to include this new type?
  • Leo Dabus
    Leo Dabus over 3 years
    if you use UIImagePNGRepresentation to get the UIImage data your data property will always return PNG. Btw in Swift 3 or later Data it is already a collection of bytes why not simply first?
  • Leo Dabus
    Leo Dabus about 2 years
    @ArunGupta a text can start with any byte. Btw a text that starts with the letter "G" could be interpreted as a gif image using this method. try String(data: Data([0x47]), encoding: .utf8)!. // G