How can you read a files MIME-type in objective-c

34,967

Solution 1

It's a bit hacky, but it should work, don't know for sure because I'm just guessing at it

There are two options:

  1. If you just need the MIME type, use the timeoutInterval: NSURLRequest.
  2. If you want the data as well, you should use the commented out NSURLRequest.

Make sure to perform the request in a thread though, since it's synchronous.

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];

Solution 2

Add MobileCoreServices framework.

Objective C:

#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);

Swift:

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
}

Solution 3

The accepted answer is problematic for large files, as others have mentioned. My app deals with video files, and loading an entire video file into memory is a good way to make iOS run out of memory. A better way to do this can be found here:

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

Code from above link:

+ (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];
}

Solution 4

Prcela solution did not work in Swift 2. The following simplified function will return the mime-type for a given file extension in 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
}

Solution 5

I was using the answer provided by slf in a cocoa app (not iPhone) and noticed that the URL request seems to be reading the entire file from disk in order to determine the mime type (not great for large files).

For anyone wanting to do this on the desktop here is the snippet I used (based on Louis's suggestion):

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

If you called that on a PDF file it would spit out: application/pdf

Share:
34,967
coneybeare
Author by

coneybeare

http://matt.coneybeare.me @coneybeare I have had a ton iOS apps under my name and my business name. I have multiple client apps, and one from an established startup. Many of my apps have been featured by Apple. I developed two iOS apps that made it into the top 100. One of the apps held the number two spot for three weeks. Another floats in/out of the paid News top 10. My apps have over 10 million downloads. I do a ton of web work too using rails for all my sites. I do much of my own design for all the sites I have created.

Updated on March 31, 2020

Comments

  • coneybeare
    coneybeare about 4 years

    I am interested in detecting the MIME-type for a file in the documents directory of my iPhone application. A search through the docs did not provide any answers.

  • coneybeare
    coneybeare almost 15 years
    this is an upload from a user into the app. I am using cocoaHTTPServer to allow a file upload via web-browser into the device. I want to check that the file is of the right type before working with it on the device.
  • KeremV
    KeremV almost 15 years
    Sure, I figured as much. What I am saying is that it is a very atypical need compared to an OS where files come and go independent of apps, so it is not suprising there is little support for it.
  • KeremV
    KeremV almost 15 years
    You cannot spawn processes on the iPhone, fork() is not allowed in sandboxed apps, so using file is not viable. Also, I assume you mean trust the server, not the browser. A http server sends the mimetype of a file. That may be viable if he is getting the files from an http server, which is not clear.
  • KeremV
    KeremV almost 15 years
    Actually he mentioned he is running an http server in the app in a comment. Depending on how the file is being sent the mimetype may or may not be transmitted.
  • Marin Todorov
    Marin Todorov over 13 years
    Hats down for the idea, but 2 negative points for the typos which make the code produce errors
  • geon
    geon over 12 years
    Unfortunately, in iOS apps, this will crash on large files, such as videos that don't fit in memory.
  • slf
    slf over 12 years
    @geon good point, +1. I never said it was a perfect solution, only a hack
  • Admin
    Admin about 12 years
    @geon you may skip data loading in NSURLConnection's delegate and analyze response in connection:didReceiveResponse:
  • brainjam
    brainjam almost 11 years
    I would have thought that setting the fileUrlRequest HTTPMethod to @"HEAD" would eliminate the file load into NSData, but that doesn't seem to be the case.
  • Daniel Farrell
    Daniel Farrell over 10 years
    Is this code just interpreting the mime-type from the file extension or is it actually reviewing some of header information in the file?
  • Daniel Farrell
    Daniel Farrell over 10 years
    This is nice a short! Is this code just interpreting the mime-type from the file extension or is it actually reviewing some of header information in the file? For example if I renamed a PDF to end in .txt would it return as a text file?
  • slf
    slf over 10 years
    @boyfarrell in truth I think it's a little of both en.wikipedia.org/wiki/Uniform_Type_Identifier
  • Krešimir Prcela
    Krešimir Prcela over 10 years
    no. It recognises mime type only by given extension.
  • Basheer_CAD
    Basheer_CAD over 9 years
    yep, your answer is better than the selected above
  • Fitter Man
    Fitter Man about 9 years
    On MacOS, this solution appears to load the data no matter what I do. There is another answer here that solves this without invoking a system utility.
  • WeZZard
    WeZZard about 9 years
    Sorry, but I think the type of cachePolicy in [NSURLRequest -initWithURL:cachePolicy:timeoutInterval:] is NSURLRequestCachePolicy but not NSURLCacheStoragePolicy. But anyway, thanks for your posting.
  • Bryan P
    Bryan P over 8 years
    I would like to add, there are some files, like docx and doc files that are not recognized as document
  • Yogi
    Yogi almost 8 years
    @Vineeth: Obviously it won't work. But to make it ARC compatible we can just remove autorelease call and add @autoreleasepool{} block to the function.
  • Tommy
    Tommy over 7 years
    This is really simple but exactly does the function! Thank you @Prcela!
  • Itachi
    Itachi over 7 years
    It depends on the file path extension, actually. Doesn't work for plain_file.abcd which returns null.
  • kemdo
    kemdo over 6 years
    it return nil for URL : file:///private/var/mobile/Containers/Data/Application/7FB4F‌​ACE-B83E-44D6-B7AD-6‌​AA54D25F3FF/Document‌​s/Inbox/pdf-5.pdf
  • Ky -
    Ky - over 5 years
    @WeZZard I saw that, and noted that it translates to NSURLRequestReturnCacheDataElseLoad. Not sure if that's the intended behavior...
  • Ky -
    Ky - over 5 years
    @kemdo instead of passing it that whole URL, just pass it the extension. That is, instead of mimeType(fileExtension: myUrl), use mimeType(fileExtension: myUrl.pathExtension)
  • Itachi
    Itachi over 2 years
    Did someone test the time performace?