سؤال

أقوم بتحميل صورة من عنوان URL الذي توفره طرف ثالث. لا يوجد تمديد ملف (أو اسم ملف لهذه المسألة) على عنوان URL (لأنه عنوان URL غامض). يمكنني أخذ البيانات من هذا (في شكل NSDATA) وتحميلها في uiimage وعرضها بشكل جيد.

أريد أن أستمر في هذه البيانات إلى ملف. ومع ذلك ، لا أعرف ما هو التنسيق الذي يوجد فيه البيانات (PNG ، JPG ، BMP)؟ أفترض أنها JPG (لأنها صورة من الويب) ولكن هل هناك طريقة برمجية لمعرفة ذلك بالتأكيد؟ لقد نظرت حول Stackoverflow وفي الوثائق ولم أتمكن من العثور على أي شيء.

تيا.


تحرير: هل أحتاج حقًا إلى امتداد الملف؟ أنا أستمر في تخزين خارجي (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., ، إليك طريقة أكثر دقة ودقيقة للتنبؤ بنوع MIME للصورة استنادًا إلى التوقيع. كان الرمز مستوحى إلى حد كبير من 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

}

تتعرف الطريقة أعلاه على أنواع الصور التالية:

  • 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 (تيف ، تيف)
  • image/png (بي إن جي)
  • image/jp2 (JP2)

لسوء الحظ ، لا توجد طريقة بسيطة للحصول على هذا النوع من المعلومات من أ UIImage مثيل ، لأنه لا يمكن الوصول إلى بيانات BITMAP المغلفة.

يتم تعيين حل 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 رأس يحتوي على أي شيء مفيد؟ (أتصور أن ذلك سيحدث لأن المتصفح ربما يكون قادرًا على عرض الصورة بشكل صحيح ، ويمكن أن يفعل ذلك فقط إذا تم تعيين نوع المحتوى بشكل مناسب)

نسخة سويفت 3:

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

إذا كان الأمر مهمًا لك حقًا ، فأعتقد أنه سيتعين عليك فحص Bytestream. سيبدأ JPEG مع بايت FF D8. سيبدأ PNG مع 89 50 4E 47 0D 0A 1A 0A. لا أعرف ما إذا كان لدى BMP رأس مشابه ، لكنني لا أعتقد أنك من المحتمل جدًا أن تصادف تلك الموجودة على الويب في عام 2010.

لكن هل يهمك حقًا؟ ألا يمكنك التعامل معها كصورة غير معروفة وتدع الكاكاو تلمس القيام بالعمل؟

بديل للإجابة المقبولة هو التحقق image I/O frameWork. يمكنك تحقيق نموذج نوع الصورة UTI. جرب هذا:

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

على سبيل المثال ، فإن UTI من صورة GIF هي "com.compuserve.gif" و png Image UTI هي "public.png". لكن لا يمكنك تحقيق UTI من الصورة التي image I/O frameWork لم يعترف.

تنفيذ فحص توقيع لكل تنسيق صورة معروف. فيما يلي وظيفة 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;
}

لقد صنعت مكتبة للتحقق من نوع صورة NSData:

https://github.com/sweetmandm/imageformatinspector

حولي المحسنة على أساس 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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top