Je charge une image à partir d'une URL fournie par un tiers. Il n'y a pas d'extension de fichier (ou de nom de fichier d'ailleurs) sur l'URL (car il s'agit d'une URL masquée). Je peux prendre les données de cela (sous la forme de NSData) et les charger dans un UIImage et les afficher correctement.
Je souhaite conserver ces données dans un fichier. Cependant, je ne sais pas dans quel format les données sont (PNG, JPG, BMP)? Je suppose que c'est JPG (car c'est une image du Web) mais y a-t-il un moyen programmatique de le savoir avec certitude? J'ai parcouru StackOverflow et la documentation et je n'ai rien trouvé.
TIA.
Edit: ai-je vraiment besoin de l'extension de fichier? Je le persiste sur un stockage externe (Amazon S3) mais étant donné qu'il sera toujours utilisé dans le contexte d'iOS ou d'un navigateur (qui semblent tous les deux bien interpréter les données sans extension), ce n'est peut-être pas un problème .
Si vous avez NSData pour le fichier image, vous pouvez deviner le type de contenu en regardant le premier octet:
+ (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;
}
Amélioration sur réponse de wl. , voici un moyen beaucoup plus étendu et précis de prédire le type MIME de l'image en fonction de la signature. Le code a été largement inspiré par 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
}
La méthode ci-dessus reconnaît les types d'images suivants:
image/x-ms-bmp
(bmp)image/gif
(gif)image/jpeg
(jpg, jpeg)image/psd
(psd)image/iff
(ssi)image/webp
(webp)image/vnd.Microsoft.icon
(ico)image/tiff
(tif, tiff)image/png
(png)image/jp2
(jp2)Malheureusement, il n'existe aucun moyen simple d'obtenir ce type d'informations à partir d'une instance UIImage
, car ses données bitmap encapsulées ne sont pas accessibles.
La solution de @Tai Le pour Swift 3 assigne des données entières dans un tableau d'octets. Si une image est grande, elle peut provoquer un crash. Cette solution affecte simplement un seul octet:
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
}
}
Si vous récupérez l'image à partir d'une URL, vous pouvez probablement inspecter les en-têtes de réponse HTTP. Est-ce que le Content-Type
l'en-tête contient quelque chose d'utile? (J'imagine que ce serait le cas, car un navigateur serait probablement en mesure d'afficher correctement l'image, et il ne pourrait le faire que si le type de contenu était correctement défini)
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
}
}
Une alternative de réponse acceptée consiste à vérifier l'UTI de l'image avec image I/O frameWork
. Vous pouvez obtenir le type d'image sous forme UTI. essaye ça:
CGImageSourceRef imgSrc = CGImageSourceCreateWithData((CFDataRef)data, NULL);
NSString *uti = (NSString*)CGImageSourceGetType(imgSrc);
NSLog(@"%@",uti);
Par exemple, l'UTI d'une image GIF est "com.compuserve.gif" et l'UTI d'une image PNG est "public.png". MAIS vous ne pouvez pas obtenir UTI à partir d'une image qui image I/O frameWork
n'est pas reconnu.
Si cela vous importe vraiment, je pense que vous devrez examiner le flux secondaire. Un JPEG commencera par les octets FF D8. Un PNG commencera par 89 50 4E 47 0D 0A 1A 0A. Je ne sais pas si BMP a un en-tête similaire, mais je ne pense pas que vous soyez trop susceptible de les rencontrer sur le Web en 2010.
Mais est-ce vraiment important pour vous? Ne pouvez-vous pas simplement le traiter comme une image inconnue et laisser Cocoa Touch faire le travail?
J'ai fait une bibliothèque pour vérifier le type d'image de NSData:
Implémentez une vérification de signature pour chaque format d'image connu. Voici une fonction Objective-C rapide qui fait cela pour les données 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;
}
Ma solution améliorée basée sur @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
}
}
}
Quelques exemples d'utilisation:
//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