15

I'm currently developing an iPad app where a user will enter a photo filename in a text field (as part of field notes), then later they will import their photos to the iPad's photo library. The app will access the library using ALAssetsLibrary and enumerate over the photos, looking for ones with the filename they entered in their field notes. This would be the filename given to the photo by the camera that took it. For example "DSC_0019.JPG".

Is this not possible?

I noticed that if I import photos from my camera to iPad, then open iPhoto on my Mac and look at the iPad as a camera, I can "get info" on the images held on the iPad and see the original filename I'm looking for. However this is not contained in the metadata on the iPad.

Any help would be greatly appreciated.

Here is my code:

(In working with the CFDictionary, pretty much everything is null except the Exif keys which don't have what i'm looking for)

- (void)viewDidLoad
{
    [super viewDidLoad];

    //start activity animation
    [self.activity setHidden:NO];
    [self.activity startAnimating];

    //init our arrays
    autoAssignedAssets  = [[NSMutableArray alloc] init];
    unAssignedRecords   = [[NSMutableArray alloc] init];
    unAssignedAssets    = [[NSMutableArray alloc] init];

    //setup the library
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];



    //[ BLOCK ] => assetEnumerator
    //
    void (^assetEnumerator)(ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *result, NSUInteger index, BOOL *stop) {

        if (result != nil) {

            if ([[result valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypePhoto] ) {


                //=================================================================

                ALAssetRepresentation* representation = [result defaultRepresentation];

                // create a buffer to hold the data for the asset's image
                uint8_t *buffer = (Byte*)malloc(representation.size);// copy the data from the asset into the buffer
                NSUInteger length = [representation getBytes:buffer fromOffset: 0.0  length:representation.size error:nil];

                // convert the buffer into a NSData object, free the buffer after
                NSData *adata = [[NSData alloc] initWithBytesNoCopy:buffer length:representation.size freeWhenDone:YES];

                // setup a dictionary with a UTI hint.  The UTI hint identifies the type of image we are dealing with (ie. a jpeg, png, or a possible RAW file)
                // specify the source hint
                NSDictionary* sourceOptionsDict = [NSDictionary dictionaryWithObjectsAndKeys: (id)[representation UTI] ,kCGImageSourceTypeIdentifierHint, nil];


                // create a CGImageSource with the NSData.  A image source can contain x number of thumbnails and full images.
                CGImageSourceRef sourceRef = CGImageSourceCreateWithData((CFDataRef) adata,  (CFDictionaryRef) sourceOptionsDict);

                [adata release];

                CFDictionaryRef imagePropertiesDictionary;

                // get a copy of the image properties from the CGImageSourceRef
                imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(sourceRef,0, NULL);

                //NSString *imageFilename = (NSString*)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyCIFFImageFileName);

                NSLog(@"%@", (NSDictionary *)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyExifDictionary));

                CFNumberRef imageWidth = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyPixelWidth);
                CFNumberRef imageHeight = (CFNumberRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyPixelHeight);

                int w = 0;
                int h = 0;

                CFNumberGetValue(imageWidth, kCFNumberIntType, &w);
                CFNumberGetValue(imageHeight, kCFNumberIntType, &h);

                // cleanup memory
                CFRelease(imagePropertiesDictionary);
                CFRelease(sourceRef);

                //NSLog(@"width: %d, height: %d", w, h);
                //NSLog(@"%@", imageFilename);



                //=================================================================


                //NSDictionary *metadata = [[result defaultRepresentation] metadata];
                //NSLog(@"\n\nAsset Info: %@", result);
                //NSLog(@"\n\n\n\nMetaData: %@", metadata);
                [autoAssignedAssets addObject:result];

            }//end if photo

        }//end if

    }; //end assetEnumerator block



    //[ BLOCK ] => assetGroupEnumerator
    //
    void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) =  ^(ALAssetsGroup *group, BOOL *stop) {

        if(group != nil) {
            [group enumerateAssetsUsingBlock:assetEnumerator];
        }//end if


        //now we're done, reload and stop animations
        [self.tableView reloadData];
        [self.activity stopAnimating];
        [self.activity setHidden:YES];

    }; //end assetGroupEnumerator block



    //[ BLOCK ] => failureBlock
    //
    void (^failureBlock)(NSError *) = ^(NSError *error) {

        NSString *errorTitle = [error localizedDescription];
        NSString *errorMessage = [error localizedRecoverySuggestion];
        NSString *errorFailureDesc = [error localizedFailureReason];

        NSLog(@"Error: %@, Suggestion: %@, Failure desc: %@", errorTitle, errorMessage, errorFailureDesc);

    }; //end failureBlock




    //loop over all the albums and process the pictures with the blocks above
    [library enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:assetGroupEnumerator failureBlock: failureBlock];


}//end viewDidLoad
JOM
  • 8,139
  • 6
  • 78
  • 111
bwizzy
  • 1,601
  • 2
  • 19
  • 34
  • I spent a while trying to figure out this answer (for fun... really!). The only headway I made was this question -- http://stackoverflow.com/questions/5048640/retrieving-a-filename-for-an-alasset -- and also this intriguing metadata tutorial -- http://sarofax.wordpress.com/2011/07/10/alasset-image-metadata/ . I wish I could have helped further. Apple's built-in apps are obviously doing some secret squirrel stuff there. – Michael Dautermann Oct 17 '11 at 11:00

2 Answers2

21

I was able to get an image's original filename like this:

NSString* originalFileName = [[asset defaultRepresentation] filename];
djschwartz
  • 1,236
  • 3
  • 14
  • 23
  • 1
    actually I realize now this does not give the original filename. It returns something like IMG_0101.JPG which just happened to be similar to the files I was using at the time. – djschwartz Nov 27 '11 at 02:59
  • 2
    I've found that this is actually the original filename IF you import photos to your device which is what I was concerned with. So from my Nikon I had DSC_0001.jpg which comes through with this method. – bwizzy Dec 01 '11 at 16:35
  • On my iOS 8 testing, files saved from email attachments get their original names stripped and replaced with IMG_0001.JPG and the number just increments. Not very useful. – BitBank Jan 21 '15 at 07:02
  • @BitBank : what did you do then? – Mak13 Apr 16 '15 at 12:29
  • I decided not to display the names at all; there's nothing I can do to replace a name that's been discarded by the OS. – BitBank Apr 16 '15 at 22:18
0

Extract the URL from the ALAsset, looks like this: assets-library://asset/asset.JPG?id=1000000001&ext=JPG

If you need different file names, make an internal-external paradigm:

#import <Foundation/Foundation.h>

@interface NSURL (NSURL_Asset)
- (NSURL*) toExternalForm;
- (NSURL*) fromExternalForm;
- (NSString*) toExternalFilename;    

@end

#import "NSURL+Asset.h"
#import "URLParser.h" // from http://iphone.demay-fr.net/2010/04/parsing-url-parameters-in-a-nsstring/

static NSString *const EXTERNAL_TOKEN = @"/assetExternalForm/";
@implementation NSURL (NSURL_Asset)

// assets-library://asset/asset.JPG/assetExternalForm/1000000001.JPG -> assets-library://asset/asset.JPG?id=1000000001&ext=JPG
- (NSURL*) fromExternalForm {
    if([self.scheme isEqualToString:@"assets-library"]) {
        NSRange slash = [self.absoluteString rangeOfString:EXTERNAL_TOKEN options:NSBackwardsSearch];
        if(slash.location != NSNotFound) {

            NSRange dot = [self.absoluteString rangeOfString:@"." options:NSBackwardsSearch];

            if(dot.location != NSNotFound) {
                NSString* extention = [self.absoluteString substringFromIndex:(dot.location + dot.length)];
                NSString* identifier = [self.absoluteString substringWithRange:NSMakeRange(slash.location + slash.length, dot.location - (slash.location + slash.length))];
                return [NSURL URLWithString:[NSString stringWithFormat:@"%@?id=%@&ext=%@", [self.absoluteString substringToIndex:slash.location], identifier, extention]];
            }
        }
    }
    return self;
}
// assets-library://asset/asset.JPG?id=1000000001&ext=JPG -> assets-library://asset/asset.JPG/assetExternalForm/1000000001.JPG
- (NSURL*) toExternalForm {
    if([self.scheme isEqualToString:@"assets-library"]) {
        NSRange range = [self.absoluteString rangeOfString:@"?"];
        if(range.location != NSNotFound) {
            URLParser *parser = [[[URLParser alloc] initWithURLString:self.absoluteString] autorelease];
            NSString* extention = [parser valueForVariable:@"ext"];
            NSString* identifier = [parser valueForVariable:@"id"];
            if(extention != NULL && identifier != NULL) {
                return [NSURL URLWithString:[NSString stringWithFormat:@"%@%@%@.%@", [self.absoluteString substringToIndex:range.location], EXTERNAL_TOKEN, identifier, extention]];
            }
        }
    }
    return self;
}
// assets-library://asset/asset.JPG?id=1000000001&ext=JPG -> 1000000001.JPG
- (NSString*) toExternalFilename {
    if([self.scheme isEqualToString:@"assets-library"]) {
        NSRange range = [self.absoluteString rangeOfString:@"?"];
        if(range.location != NSNotFound) {
            URLParser *parser = [[[URLParser alloc] initWithURLString:self.absoluteString] autorelease];
            NSString* extention = [parser valueForVariable:@"ext"];
            NSString* identifier = [parser valueForVariable:@"id"];

            if(extention != NULL && identifier != NULL) {
                return [NSString stringWithFormat:@"%@.%@", identifier, extention];
            }
        }
    }
    return NULL;
}
@end

Note that you do not need a filename to read the content of an ALAsset. Use the ALAsset.defaultRepresentation.getBytes method for that.

Gabriel
  • 3,039
  • 6
  • 34
  • 44