Как вы можете читать файлы MIME-типа в target-c

Меня интересует определение MIME-типа файла в каталоге документов моего приложения для iPhone. Поиск по документам не дал ответов.


person coneybeare    schedule 01.09.2009    source источник
comment
Есть еще один отличный ответ: [stackoverflow_Q][1] [1]: stackoverflow.com/questions/2439020/   -  person Cullen SUN    schedule 21.02.2012


Ответы (9)


Это немного хакерски, но это должно работать, я точно не знаю, потому что я просто догадываюсь об этом.

Есть два варианта:

  1. Если вам просто нужен тип MIME, используйте timeoutInterval: NSURLRequest.
  2. Если вам также нужны данные, вы должны использовать закомментированный NSURLRequest.

Обязательно выполняйте запрос в потоке, так как он синхронный.

NSString* filePath = [[NSBundle mainBundle] pathForResource:@"imagename" ofType:@"jpg"];
NSString* fullPath = [filePath stringByExpandingTildeInPath];
NSURL* fileUrl = [NSURL fileURLWithPath:fullPath];
//NSURLRequest* fileUrlRequest = [[NSURLRequest alloc] initWithURL:fileUrl];
NSURLRequest* fileUrlRequest = [[NSURLRequest alloc] initWithURL:fileUrl cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:.1];

NSError* error = nil;
NSURLResponse* response = nil;
NSData* fileData = [NSURLConnection sendSynchronousRequest:fileUrlRequest returningResponse:&response error:&error];

fileData; // Ignore this if you're using the timeoutInterval
          // request, since the data will be truncated.

NSString* mimeType = [response MIMEType];

[fileUrlRequest release];
person slf    schedule 09.09.2009
comment
Снимаю шляпу за идею, но 2 отрицательных балла за опечатки, из-за которых код выдает ошибки. - person Marin Todorov; 20.11.2010
comment
К сожалению, в приложениях для iOS это приведет к сбою на больших файлах, таких как видео, которые не помещаются в памяти. - person geon; 06.02.2012
comment
@geon хорошее замечание, +1. Я никогда не говорил, что это идеальное решение, только хак - person slf; 06.02.2012
comment
@geon вы можете пропустить загрузку данных в делегате NSURLConnection и проанализировать ответ в connection:didReceiveResponse: - person ; 03.04.2012
comment
Я бы подумал, что установка HTTPMethod fileUrlRequest на @HEAD устранит загрузку файла в NSData, но, похоже, это не так. - person brainjam; 02.07.2013
comment
Является ли этот код просто интерпретацией MIME-типа из расширения файла или он фактически просматривает некоторую информацию заголовка в файле? - person Daniel Farrell; 19.02.2014
comment
@boyfarrell, по правде говоря, я думаю, что это немного и en.wikipedia.org/wiki/Uniform_Type_Identifier - person slf; 19.02.2014
comment
В MacOS это решение загружает данные независимо от того, что я делаю. Здесь есть еще один ответ, который решает эту проблему без вызова системной утилиты. - person Fitter Man; 30.03.2015
comment
Извините, но я думаю, что тип cachePolicy в [NSURLRequest -initWithURL:cachePolicy:timeoutInterval:] - это NSURLRequestCachePolicy, но не NSURLCacheStoragePolicy. Но в любом случае, спасибо за вашу публикацию. - person WeZZard; 06.05.2015
comment
@WeZZard Я видел это и заметил, что это переводится как NSURLRequestReturnCacheDataElseLoad. Не уверен, что это предполагаемое поведение... - person Ky Leggiero; 27.12.2018

Добавьте структуру MobileCoreServices.

Цель С:

#import <MobileCoreServices/MobileCoreServices.h>    
NSString *fileExtension = [myFileURL pathExtension];
NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExtension, NULL);
NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);

Быстрый:

import MobileCoreServices

func mimeType(fileExtension: String) -> String? {

    guard !fileExtension.isEmpty else { return nil }

    if let utiRef = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension as CFString, nil) {
        let uti = utiRef.takeUnretainedValue()
        utiRef.release()

        if let mimeTypeRef = UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType) {
            let mimeType = MIMETypeRef.takeUnretainedValue()
            mimeTypeRef.release()
            return mimeType as String
        }
    }

    return nil
}
person Prcela    schedule 18.02.2014
comment
Это хорошая короткометражка! Является ли этот код просто интерпретацией MIME-типа из расширения файла или он фактически просматривает некоторую информацию заголовка в файле? Например, если я переименую PDF-файл, чтобы он заканчивался на .txt, вернется ли он в виде текстового файла? - person Daniel Farrell; 19.02.2014
comment
нет. Он распознает MIME-тип только по заданному расширению. - person Prcela; 20.02.2014
comment
Я хотел бы добавить, что есть некоторые файлы, такие как файлы docx и doc, которые не распознаются как документы. - person Bryan P; 22.02.2016
comment
Это действительно просто, но точно выполняет функцию! Спасибо @Prcela! - person Tommy; 25.01.2017
comment
На самом деле это зависит от расширения пути к файлу. Не работает для plain_file.abcd, который возвращает null. - person Itachi; 14.03.2017
comment
он возвращает ноль для URL-адреса: file:///private/var/mobile/Containers/Data/Application/7FB4FACE-B83E-44D6-B7AD-6AA54D25F3FF/Documents/Inbox/pdf-5.pdf - person kemdo; 17.12.2017
comment
@kemdo вместо того, чтобы передавать ему весь URL-адрес, просто передайте ему расширение. То есть вместо mimeType(fileExtension: myUrl) использовать mimeType(fileExtension: myUrl.pathExtension) - person Ky Leggiero; 20.02.2019

Как уже упоминалось, принятый ответ проблематичен для больших файлов. Мое приложение работает с видеофайлами, и загрузка всего видеофайла в память — хороший способ заставить iOS исчерпать память. Лучший способ сделать это можно найти здесь:

https://stackoverflow.com/a/5998683/1864774

Код по ссылке выше:

+ (NSString*) mimeTypeForFileAtPath: (NSString *) path {
  if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
    return nil;
  }
  // Borrowed from https://stackoverflow.com/questions/5996797/determine-mime-type-of-nsdata-loaded-from-a-file
  // itself, derived from  https://stackoverflow.com/questions/2439020/wheres-the-iphone-mime-type-database
  CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)[path pathExtension], NULL);
  CFStringRef mimeType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType);
  CFRelease(UTI);
  if (!mimeType) {
    return @"application/octet-stream";
  }
  return [NSMakeCollectable((NSString *)mimeType) autorelease];
}
person Adam Lockhart    schedule 19.05.2014
comment
да, ваш ответ лучше, чем выбранный выше - person Basheer_CAD; 05.03.2015
comment
@Vineeth: Очевидно, это не сработает. Но чтобы сделать его совместимым с ARC, мы можем просто удалить вызов autorelease и добавить к функции блок @autoreleasepool{}. - person Yogi; 09.08.2016

решение Prcela не работало в Swift 2. Следующая упрощенная функция вернет тип mime для данного расширения файла в Swift 2:

import MobileCoreServices

func mimeTypeFromFileExtension(fileExtension: String) -> String? {
    guard let uti: CFString = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension as NSString, nil)?.takeRetainedValue() else {
        return nil
    }

    guard let mimeType: CFString = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() else {
        return nil
    }

    return mimeType as String
}
person dreamlab    schedule 26.11.2015

Я использовал ответ, предоставленный slf в приложении какао (не iPhone), и заметил, что запрос URL-адреса, похоже, считывает весь файл с диска, чтобы определить тип mime (не подходит для больших файлов).

Для тех, кто хочет сделать это на рабочем столе, вот фрагмент, который я использовал (на основе предложения Луи):

NSString *path = @"/path/to/some/file";

NSTask *task = [[[NSTask alloc] init] autorelease];
[task setLaunchPath: @"/usr/bin/file"];
[task setArguments: [NSArray arrayWithObjects: @"-b", @"--mime-type", path, nil]];

NSPipe *pipe = [NSPipe pipe];
[task setStandardOutput: pipe];

NSFileHandle *file = [pipe fileHandleForReading];

[task launch];
[task waitUntilExit];
if ([task terminationStatus] == YES) {
    NSData *data = [file readDataToEndOfFile];
    return [[[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding] autorelease];
} else {
    return nil;
}

Если бы вы назвали это в файле PDF, он бы выплюнул: application/pdf

person danw    schedule 03.08.2011

Основываясь на ответе Лоуренса Дола/slf выше, я решил, что NSURL загружает весь файл в память, вырезая первый несколько байтов в заголовок и получение MIMEType этого. Я не тестировал его, но, вероятно, он тоже быстрее.

+ (NSString*) mimeTypeForFileAtPath: (NSString *) path {
    // NSURL will read the entire file and may exceed available memory if the file is large enough. Therefore, we will write the first fiew bytes of the file to a head-stub for NSURL to get the MIMEType from.
    NSFileHandle *readFileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
    NSData *fileHead = [readFileHandle readDataOfLength:100]; // we probably only need 2 bytes. we'll get the first 100 instead.

    NSString *tempPath = [NSHomeDirectory() stringByAppendingPathComponent: @"tmp/fileHead.tmp"];

    [[NSFileManager defaultManager] removeItemAtPath:tempPath error:nil]; // delete any existing version of fileHead.tmp
    if ([fileHead writeToFile:tempPath atomically:YES])
    {
        NSURL* fileUrl = [NSURL fileURLWithPath:path];
        NSURLRequest* fileUrlRequest = [[NSURLRequest alloc] initWithURL:fileUrl cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:.1];

        NSError* error = nil;
        NSURLResponse* response = nil;
        [NSURLConnection sendSynchronousRequest:fileUrlRequest returningResponse:&response error:&error];
        [[NSFileManager defaultManager] removeItemAtPath:tempPath error:nil];
        return [response MIMEType];
    }
    return nil;
}
person got nate    schedule 04.09.2015

В Mac OS X это будет обрабатываться через LaunchServices и UTI. На iPhone они недоступны. Поскольку единственный способ попасть в вашу «песочницу» — это поместить их туда, большинство приложений обладают внутренними знаниями о данных любого файла, который они могут прочитать.

Если вам нужна такая функция, вы должны отправить запрос на функцию в Apple.

person Louis Gerbarg    schedule 01.09.2009
comment
это загрузка от пользователя в приложение. Я использую CocoHTTPServer, чтобы разрешить загрузку файла через веб-браузер на устройство. Я хочу убедиться, что файл имеет правильный тип, прежде чем работать с ним на устройстве. - person coneybeare; 02.09.2009
comment
Конечно, я так и думал. Я говорю о том, что это очень нетипичная потребность по сравнению с ОС, в которой файлы приходят и уходят независимо от приложений, поэтому неудивительно, что ее мало поддерживают. - person Louis Gerbarg; 02.09.2009

Я не уверен, какова практика на iPhone, но если вам разрешено, я бы использовал здесь философию UNIX: используйте программу file, которая является стандартным способом определения типа файла в операционной системе UNIX. Он включает в себя обширную базу данных магических маркеров для определения типа файла. Поскольку file, вероятно, не поставляется на iPhone, вы можете включить его в свой пакет приложений. Может существовать библиотека, реализующая функциональность file.

Кроме того, вы можете доверять браузеру. Браузеры отправляют тип MIME, который они угадали, где-то в заголовках HTTP. Я знаю, что могу легко получить информацию о типе MIME в PHP. Это, конечно, зависит от того, готовы ли вы доверять клиенту.

person Ivan Vučica    schedule 09.09.2009
comment
Вы не можете создавать процессы на iPhone, fork() не разрешен в изолированных приложениях, поэтому использование файла нецелесообразно. Кроме того, я предполагаю, что вы имеете в виду доверие серверу, а не браузеру. HTTP-сервер отправляет mimetype файла. Это может быть целесообразно, если он получает файлы с http-сервера, что неясно. - person Louis Gerbarg; 10.09.2009
comment
На самом деле он упомянул, что у него есть http-сервер в приложении в комментарии. В зависимости от того, как файл отправляется, тип mime может передаваться или не передаваться. - person Louis Gerbarg; 10.09.2009

Убедитесь, что вы импортируете основные службы

import <CoreServices/CoreServices.h>

в вашем файле.

person Hari Narayanan    schedule 30.03.2020