簡體   English   中英

從 NSData 或 UIImage 中查找圖像類型

[英]Finding image type from NSData or UIImage

我正在從第三方提供的 URL 加載圖像。 URL 上沒有文件擴展名(或文件名)(因為它是一個模糊的 URL)。 我可以從中獲取數據(以 NSData 的形式)並將其加載到 UIImage 中並正常顯示。

我想將此數據保存到文件中。 但是,我不知道數據是什么格式(PNG、JPG、BMP)? 我假設它是 JPG(因為它是來自網絡的圖像)但是否有確定的程序化方式? 我環顧了 StackOverflow 和文檔,但沒有找到任何東西。

TIA。


編輯:我真的需要文件擴展名嗎? 我將它持久化到外部存儲(Amazon S3),但考慮到它將始終在 iOS 或瀏覽器的上下文中使用(兩者似乎都可以在沒有擴展的情況下解釋數據)也許這不是問題.

如果您有圖像文件的 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;
}

改進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

}

上述方法識別以下圖像類型:

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

不幸的是,沒有簡單的方法可以從UIImage實例中獲取此類信息,因為無法訪問其封裝的位圖數據。

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

如果您從 URL 檢索圖像,那么您大概可以檢查 HTTP 響應標頭。 Content-Type標頭是否包含任何有用的信息? (我想它會,因為瀏覽器可能能夠正確顯示圖像,並且只有在內容類型設置正確的情況下才能做到這一點)

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

接受答案的替代方法是使用image I/O frameWork檢查圖像的 UTI。 您可以通過 UTI 實現圖像類型。 嘗試這個:

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

例如,GIF 圖像的 UTI 是“com.compuserve.gif”,PNG 圖像的 UTI 是“public.png”。但是您無法從image I/O frameWork無法識別的image I/O frameWork實現 UTI。

我基於@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
        }
    }
}

一些使用示例:

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

要從UIImage獲取圖像類型,您可以從底層 Quartz 圖像數據中獲取類型標識符 (UTI):

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

要從 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)
}

我做了一個庫來檢查 NSData 的圖像類型:

https://github.com/sweetmandm/ImageFormatInspector

如果這對您來說真的很重要,我相信您必須檢查字節流。 JPEG 將以字節 FF D8 開頭。 PNG 將以 89 50 4E 47 0D 0A 1A 0A 開頭。 我不知道 BMP 是否有類似的標題,但我認為您在 2010 年不太可能在網絡上遇到這些標題。

但這對你來說真的很重要嗎? 你不能把它當作一個未知的圖像,讓 Cocoa Touch 來完成工作嗎?

對每個已知的圖像格式實施簽名檢查。 這是一個快速的 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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM