I'm building an app in React Native, and I need to get an image from the Photo Library as base64 using the photo uri (e.g. photos://A2B9B28B-9A3E-4190-BCA0-B11F1E587085/L0/002). I was originally using the Asset Library but I've updated my app to use the new Photo Library and I'm struggling to get it to work.
My old Asset Library method was:
{
NSURL *url = [[NSURL alloc] initWithString:input];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:url resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *rep = [asset defaultRepresentation];
CGImageRef imageRef = [rep fullScreenImage];
NSData *imageData = UIImagePNGRepresentation([UIImage imageWithCGImage:imageRef]);
NSString *base64Encoded = [imageData base64EncodedStringWithOptions:0];
callback(#[[NSNull null], base64Encoded]);
} failureBlock:^(NSError *error) {
NSLog(#"that didn't work %#", error);
callback(#[error]);
}];
}
It is basicly the same as the code you already have, it has just been split into two parts. First get a PHAsset using an id, then get the image from that asset using the PHImageManager.
To get the PHAsset from the library use
[+ (PHFetchResult<PHAsset *> *)fetchAssetsWithALAssetURLs:(NSArray<NSURL *> *)assetURLs options:(nullable PHFetchOptions *)options;
This method is synchronous and immediately returns an array of PHAsset but these assets don't contain any image data.
next use
[PHImageManager defaultManager] to get the image. This is asynchronous like the ALAssetsLibrary method. use - (PHImageRequestID)requestImageDataForAsset:(PHAsset *)asset options:(PHImageRequestOptions *)options resultHandler:(void (^)(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info))resultHandler;
The code should be something like this (obviously you have to fill in your error codes and error domain for your application)):
PHFetchResult* results = [PHAsset fetchAssetsWithLocalIdentifiers:#[input] options:nil];
PHAsset *asset = [results firstObject];
if (asset) {
[[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
if (imageData){
NSString *base64Encoded = [imageData base64EncodedStringWithOptions:0];
callback(#[[NSNull null], base64Encoded]);
}else{
NSError* error = info[PHImageErrorKey];
if (!error) {
error = [NSError errorWithDomain:#"" code:-1 userInfo:#{NSLocalizedDescriptionKey:#"image not found"}];
}
callback(nil, error);
}
}];
}else{
NSError* error = [NSError errorWithDomain:#"" code:-1 userInfo:#{NSLocalizedDescriptionKey:#"asset not found"}];
callback(nil, error);
}
You can see the api in PHAsset+ (PHFetchResult<PHAsset *> *)fetchAssetsWithALAssetURLs:(NSArray<NSURL *> *)assetURLs options:(nullable PHFetchOptions *)options;
Related
i write this code but App crash due to High memory consumptions.
-(void)saveToDir
{
[[PHImageManager defaultManager] requestAVAssetForVideo:[assets objectAtIndex:indexPath.item] options:[PHVideoRequestOptions new] resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
dispatch_async(dispatch_get_main_queue(), ^
{
if ([asset isKindOfClass:[AVURLAsset class]])
{
NSURL *url = [(AVURLAsset*)asset URL];
urlFromLibrary=url;
AVAsset *asset=[selectedImageArray objectAtIndex:i];
NSURL *url = [(AVURLAsset*)asset URL];
NSString *filename = [[url path] lastPathComponent];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:filename];
NSArray *array=[NSArray arrayWithObjects:url,filePath, nil];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
if (data)
{
if([data writeToFile:filePath atomically:YES])
{
NSLog(#“write successfull”);
}
else{
NSLog(#“write failed”);
}
}
// [self downloadVideoAndSave:url :filePath];
}
}];
i wrote this above code but app crash with memory issue so better response
I am trying to achieve a simple task like converting a UIImage to NSData, for AVPlayerItem that is returned to me when I select a video from the PHImageManager. What might be an equivalent of the UIImagePNGRepresentation to convert video in data:
PHVideoRequestOptions *videoRequestOptions = [[PHVideoRequestOptions alloc] init];
videoRequestOptions.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
videoRequestOptions.version = PHVideoRequestOptionsVersionOriginal;
[[PHImageManager defaultManager] requestPlayerItemForVideo:asset options:videoRequestOptions resultHandler:^(AVPlayerItem *item, NSDictionary *info)
{
//?
}];
Whereas the UIImage goes like this:
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:imageRequestOptions resultHandler:^(UIImage *result, NSDictionary *info)
{
NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(result)]; //<==THIS
}
The solution is to get the URL from the AVPlayerItem asset, then create NSData from that URL:
PHAsset *asset = ...
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
[[PHImageManager defaultManager] requestPlayerItemForVideo:asset options:options resultHandler:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) {
NSURL *fileURL = [(AVURLAsset *)playerItem.asset URL];
NSData *videoData = [NSData dataWithContentsOfURL:fileURL];
NSLog(#"tmpData Size: %lu",tmpData.length);
}];
and another way is to use 'requestAVAssetForVideo:asset' ....
PHFetchOptions *fetchOption = [[PHFetchOptions alloc]init];
if ([fetchOption respondsToSelector:#selector(setFetchLimit:)]) {
[fetchOption setFetchLimit:1];
}
PHAsset *asset = [PHAsset fetchAssetsWithLocalIdentifiers:#[#"<Video-localIdentifier>"] options:fetchOption].firstObject;
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc]init];
options.version = PHVideoRequestOptionsVersionOriginal;
options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
[[PHImageManager defaultManager] requestAVAssetForVideo:asset
options:options
resultHandler:
^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
AVURLAsset *urlAsset = (AVURLAsset *)asset;
NSData *videoData = [NSData dataWithContentsOfURL:urlAsset.URL];
if (videoData) {
NSLog(#"videoData Size: %lu", videoData.length);
}
}];
I want to get list of thumbnails of all my camera roll photos, but for some photos I can't read NSData.
PHFetchResult *allPhotosResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil];
for(PHAsset *asset in allPhotosResult) {
[[PHImageManager defaultManager]
requestImageForAsset:asset
targetSize:CGSizeMake(80, 80)
contentMode:PHImageContentModeAspectFill
options:nil
resultHandler:^(UIImage *result, NSDictionary *info) {
NSData *data = UIImagePNGRepresentation(result); // nil for some photos
NSString *base = [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
}];
}
What is the best way to upload videos from gallery using Photos framework?
Before I used ALAssetRepresentation and next method:
- (NSUInteger)getBytes:(uint8_t *)buffer fromOffset:(long long)offset length:(NSUInteger)length error:(NSError **)error;
this allowed to upload file without first copying it to app temp directory. Don’t see any alternatives in Photos framework. Only way seems to use AVAssetExportSession -> export to local directory -> upload, but this requires additional storage space (could be a problem, if video is too big)
Seems the only valid way is to request AVAsset from PHImageManager, and check if returned asset is AVURLAsset. In this case URL can be used to directly access file and get needed chunk of bytes:
[[PHImageManager defaultManager] requestAVAssetForVideo:videoAsset options:nil resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
if ([asset isKindOfClass:[AVURLAsset class]]) {
NSURL *URL = [(AVURLAsset *)asset URL];
// use URL to get file content
}
}];
This will not work with slow motion videos, because AVComposition instead of AVURLAsset is returned. Possible solution is to use PHVideoRequestOptionsVersionOriginal video file version:
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHVideoRequestOptionsVersionOriginal;
[[PHImageManager defaultManager] requestAVAssetForVideo:videoAsset options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
if ([asset isKindOfClass:[AVURLAsset class]]) {
NSURL *URL = [(AVURLAsset *)asset URL];
// use URL to get file content
}
}];
And to get fullsize image url:
PHContentEditingInputRequestOptions *options = [[PHContentEditingInputRequestOptions alloc] init];
options.canHandleAdjustmentData = ^BOOL(PHAdjustmentData *adjustmentData) {
return YES;
};
[imageAsset requestContentEditingInputWithOptions:options completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
// use contentEditingInput.fullSizeImageURL
}];
The Photo Framework for iOS 8 is able to return a URL for images using the PHImageFileURLKey. However I can't seem to read this URL. I only need read access. So far, any place I attempt to use the URL given, it is treated as though the file doesn't exist at all. How can I get data using the URL?
Code that successfully returns PHAssets and a URL:
PHFetchResult* phFetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil]
PHImageManager *manager = [PHImageManager defaultManager];
PHImageRequestOptions* options = [[PHImageRequestOptions alloc] init];
options.synchronous = YES; // Force sequential work. We have nothing to do until this block returns.
PHAsset* asset = [phFetchResult objectAtIndex:index];
[manager requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:options resultHandler:^(UIImage *resultImage, NSDictionary *info)
{
_image = resultImage;
NSURL* fileURL = [info objectForKey:#"PHImageFileURLKey"];
_imageLocation = [fileURL relativePath];
}];
Code showing nothing found:
NSURL* fileURL = [info objectForKey:#"PHImageFileURLKey"];
NSLog(#"File exist?: %d", [[NSFileManager defaultManager] fileExistsAtPath:[fileURL relativePath]]);
My current workaround is to take the UIImage given and save it to the /tmp directory and then use that URL. It's not an ideal solution and I'd really like to know the purpose of the PHImageFileURLKey
I research on PHImageFileURLKey and i think there is no way to find image data by PHImageFileURLKey but you can find your image data by using asset local identifier instead of PHImageFileURLKey.
My code for your reference:
You can use your asset's local identifier and will get the image in response.
PHFetchResult *savedAssets = [PHAsset fetchAssetsWithLocalIdentifiers:#[asset.localIdentifier] options:nil];
[savedAssets enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) {
//this gets called for every asset from its localIdentifier you saved
PHImageRequestOptions * imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.synchronous = YES;
imageRequestOptions.deliveryMode = PHImageRequestOptionsResizeModeFast;
[[PHImageManager defaultManager]requestImageForAsset:asset targetSize:CGSizeMake(50,50) contentMode:PHImageContentModeAspectFill options:imageRequestOptions resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
NSLog(#"get image from result");
}];
}];