I have a method which retrieves information about a PHAsset, and it works, but when it comes to reading information of hundreds of assets it can be quite slow.
What is the best way to read information about an asset such as filesize and meta-data?
My current code is:
- (void) getAssetInfo: (NSUInteger*) assetIndex {
NSNumber *_assetIndex = [NSNumber numberWithUnsignedInteger: assetIndex];
PHFetchOptions *fetchOptions;
fetchOptions.sortDescriptors = #[ [NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:YES], ];
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithOptions:fetchOptions];
PHAsset *asset = [fetchResult objectAtIndex: assetIndex];
PHImageManager *imageManager = [PHImageManager defaultManager];
PHImageRequestOptions *options = [[PHImageRequestOptions alloc]init];
options.synchronous = YES;
options.version = PHImageRequestOptionsVersionCurrent;
[asset requestContentEditingInputWithOptions:options
completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
NSString *fileInfo;
NSString *assetURL = nil;
if (contentEditingInput.avAsset != nil){
AVURLAsset *avurlasset_ = (AVURLAsset*) contentEditingInput.avAsset;
assetURL = [avurlasset_.URL absoluteString];
} else {
assetURL = [contentEditingInput.fullSizeImageURL absoluteString];
}
if (assetURL != nil){
uint64_t fileSize;
NSString *filePathFull = [assetURL substringWithRange: NSMakeRange(7, [assetURL length] - 7)];
NSFileManager * filemanager = [[NSFileManager alloc]init];
if([filemanager fileExistsAtPath:filePathFull]){
fileSize = [[filemanager attributesOfItemAtPath:filePathFull error:nil] fileSize];
}
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy,MM,dd,HH,mm,ss"];
NSArray *assembled_FI = [NSArray arrayWithObjects:
(NSNumber*) _assetIndex,
assetURL,
assetURL.lastPathComponent,
(NSNumber*)[NSNumber numberWithUnsignedLongLong: fileSize],
(NSString*) [dateFormatter stringFromDate:asset.creationDate],
nil];
}
}];
}
There is a small library for Swift 3 recently added to GitHub called AssetManager.
It simplifies the access to assets and allows for very fast data extraction.
The link to AssetManager can be found here: https://github.com/aidv/AssetManager
Related
I have an app where I am picking a video from a photo library and I want to get the date and time it was created (and if possible the location it was taken) from its metadata so I can use it for some labeling. I can do it for an image picked from the photo library but it doesn't work with a video. Here is what I have for the image but I can't seem to adapt it to a video.
So I did some adapting like this:
//I get the video from the photo library
if (picker.sourceType ==UIImagePickerControllerSourceTypePhotoLibrary) {
NSLog(#"info:%#",info);
NSURL * movieURL = [info valueForKey:UIImagePickerControllerMediaURL] ;
NSData * movieData = [NSData dataWithContentsOfURL:movieURL];
//I save it to the documentsDirectory and get an image at the half way point
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [documentPaths objectAtIndex:0];
NSString *slash = [documentsDirectory stringByAppendingPathComponent:#"/"];
NSString *documentsDirectory2 = [slash stringByAppendingPathComponent:self.user.text];
NSString *documentsDirectory21 = [documentsDirectory2 stringByAppendingPathComponent:#"/Movie"];
if (![[NSFileManager defaultManager] fileExistsAtPath:documentsDirectory21])
[[NSFileManager defaultManager] createDirectoryAtPath:documentsDirectory21 withIntermediateDirectories:NO attributes:nil error:&error];
NSString *fullPath = [documentsDirectory21 stringByAppendingPathComponent:[[self imageNameTextField]text]];
fullPath = [fullPath stringByAppendingFormat:#".mp4"];
[ movieData writeToFile:fullPath atomically:YES];
AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:movieURL options:nil];
//this is what I changed
PHAsset *theAsset = [info valueForKey:UIImagePickerControllerMediaURL] ;
NSLog(#"creationDate1:%#",theAsset);
NSLog(#"creationDate2:%#",anAsset.creationDate.value);
NSDate *creationDate =(NSDate *)anAsset.creationDate.value;
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMMM-dd-yyyy"];
dayString = [dateFormatter stringFromDate:creationDate];
NSDateFormatter *dateFormatter2=[[NSDateFormatter alloc] init];
[dateFormatter2 setDateFormat:#"hh:mm a"];
timeString = [dateFormatter2 stringFromDate:creationDate];
if ([[anAsset tracksWithMediaType:AVMediaTypeVideo] count] > 0) {
AVAssetImageGenerator *imageGenerator =
[AVAssetImageGenerator assetImageGeneratorWithAsset:anAsset];
Float64 durationSeconds = CMTimeGetSeconds([anAsset duration]);
CMTime midpoint = CMTimeMakeWithSeconds(durationSeconds/2.0, 600);
NSError *error;
CMTime actualTime;
CGImageRef halfWayImage = [imageGenerator copyCGImageAtTime:midpoint actualTime:&actualTime error:&error];
if (halfWayImage != NULL) {
NSString *actualTimeString = (NSString *)CFBridgingRelease(CMTimeCopyDescription(NULL, actualTime));
NSString *requestedTimeString = (NSString *)CFBridgingRelease(CMTimeCopyDescription(NULL, midpoint));
NSLog(#"Got halfWayImage: Asked for %#, got %#", requestedTimeString, actualTimeString);
myImage.image = [UIImage imageWithCGImage:halfWayImage scale:1.0 orientation:UIImageOrientationRight];
// Do something interesting with the image.
CGImageRelease(halfWayImage);
}
}
what I had here before I discarded.
So I finally figured this out. By putting the following code after
AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:movieURL options:nil];
PHAsset *theAsset = [info valueForKey:UIImagePickerControllerMediaURL] ;
NSLog(#"creationDate1:%#",theAsset);
NSLog(#"creationDate2:%#",anAsset.creationDate.value);
NSDate *creationDate =(NSDate *)anAsset.creationDate.value;
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMMM-dd-yyyy"];
dayString = [dateFormatter stringFromDate:creationDate];
NSDateFormatter *dateFormatter2=[[NSDateFormatter alloc] init];
[dateFormatter2 setDateFormat:#"hh:mm a"];
timeString = [dateFormatter2 stringFromDate:creationDate];
I've changed my original post to reflect the changes.
Now if I could just get the metadata for location from the asset I'd be all set.
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.sortDescriptors = #[[NSSortDescriptor
sortDescriptorWithKey:#"creationDate" ascending:YES]];
_assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
if (_assetsFetchResults.count > 0 ) {
_imageManager = [[PHCachingImageManager alloc] init];
[_arrgalleryImages setObject:[UIImage imageNamed:#"Photo"] atIndexedSubscript:0];
for (int k = 0; k <= 4; k++) {
PHAsset *asset = [_assetsFetchResults objectAtIndex:k];
[_imageManager requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsPath = [paths objectAtIndex:0];
NSString* filePath = [documentsPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%d.png", k]];
path = filePath;
[imageData writeToFile:path atomically:YES];
[_arrgalleryImages addObject:path];
}];
}
}
Not getting gallery images for the first time, i.e _assetsFetchResults.count gives nil for the first time.
Im using the following code to access filename of the image that I gotta upload. I need both filename alongside the file path and size.
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *imageAsset)
{
ALAssetRepresentation *imageRep = [imageAsset defaultRepresentation];
NSLog(#"[imageRep filename] : %#", [imageRep filename]);
[_imageNameArray addObject:[imageRep filename]];
};
[_uploadTbleView reloadData];
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:refURL resultBlock:resultblock failureBlock:nil];
The problem that Im facing is,
Xcode throws a warning message that ALAssetsLibraryAssetForURLResultBlock is deprecated. How can I replace the above code to get the filename ?
I tried using PHAsset but every time when Im selecting the file, it has got the same file name called asset.jpg for all image files.
You can try this:
PHAsset *asset = nil;
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:YES]];
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
if (fetchResult != nil && fetchResult.count > 0) {
// get last photo from Photos
asset = [fetchResult lastObject];
}
if (asset) {
// get photo info from this asset
PHImageRequestOptions * imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.synchronous = YES;
[[PHImageManager defaultManager]
requestImageDataForAsset:asset
options:imageRequestOptions
resultHandler:^(NSData *imageData, NSString *dataUTI,
UIImageOrientation orientation,
NSDictionary *info)
{
NSLog(#"info = %#", info);
if ([info objectForKey:#"PHImageFileURLKey"]) {
// path looks like this -
// file:///var/mobile/Media/DCIM/###APPLE/IMG_####.JPG
NSURL *path = [info objectForKey:#"PHImageFileURLKey"];
}
}];
}
I wrote simple library to get photos from camera roll. Unfortunately can't read some of them. I can't preview or convert to NSData
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.includeAssetSourceTypes = PHAssetSourceTypeUserLibrary;
PHFetchResult *allPhotosResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:options];
PHImageRequestOptions *requestOptionForPhotos = [[PHImageRequestOptions alloc] init];
requestOptionForPhotos.networkAccessAllowed = YES;
for(PHAsset *asset in allPhotosResult) {
[[PHImageManager defaultManager]
requestImageForAsset:asset
targetSize:CGSizeMake(100, 100)
contentMode:PHImageContentModeAspectFill
options:requestOptionForPhotos
resultHandler:^(UIImage *result, NSDictionary *info) {
NSData *data = UIImagePNGRepresentation(result);
NSString *base = [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; // for some of photos there is nil
}];
}
I need to get information about an asset without loading it into memory which might take a long time if the asset is very large.
As I will be iterating through all assets available in my device, it might take a very long time.
I am using the following code to load an asset in to an NSData object and also retrieve some information about it:
- (NSData*)getAssetInfo: (NSUInteger*) assetIndex {
PHFetchOptions *fetchOptions;
fetchOptions.sortDescriptors = #[ [NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:YES], ];
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithOptions:fetchOptions];
__block NSData *iData = nil;
PHAsset *asset = [fetchResult objectAtIndex: assetIndex];
PHImageManager *imageManager = [PHImageManager defaultManager];
PHImageRequestOptions *options = [[PHImageRequestOptions alloc]init];
options.synchronous = YES;
options.version = PHImageRequestOptionsVersionCurrent;
#autoreleasepool {
[imageManager requestImageDataForAsset:asset options:options resultHandler:^(NSData *imgData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
//NSLog(#"requestImageDataForAsset returned info(%#)", info);
NSURL *url = [info valueForKey: #"PHImageFileURLKey"];
NSString *urlAsString = [url absoluteString];
gAssetFilename = [urlAsString lastPathComponent];
gAssetCreationDate = asset.creationDate;
iData = imgData;
}];
}
if (iData != nil) {
return iData;
}
else{
NSLog(#"OOPS!");
return nil;
}
}
This works but as I said, it might take a very long time.
Is it possible to get the size of the asset as well other information about the asset without first fully load it into memory?