[英]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 的圖像類型:
如果這對您來說真的很重要,我相信您必須檢查字節流。 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.