I am using the multiple selection. I would like to know is there any way to make the tmp file to be permanent, because I want to use these selected photos in the future, at least after users restart the app. If you can just return the tag of images inside CameraRoll, like Asset-Library://.........., that would be nice!
I have read your code, it seems like it always executes the persistFile method, even if I just only use the multiple-selection, instead of cropping. May be you should see if the cropping property is enabled. If it is disabled, then we shouldn't create any temp files. It is unnecessary and time-consuming. Especially if you are trying to select 1000 photos or 100 videos, but without using compression and cropping.
In order words, if the user still wants to get a read-write file, they can use react-native-fs or react-native-fetch-blob to easily create a temp file.
Another solution is: simply create a new method call: getCacheDirectory, the cacheDir is not the same as tmpDir because it will not be purged by the system automatically.
- (NSString*) getCacheDirectory {
NSString *TMP_DIRECTORY = @"react-native-image-crop-picker/";
NSString *cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *tmpFullPath = [cacheDir stringByAppendingString:TMP_DIRECTORY];
BOOL isDir;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:tmpFullPath isDirectory:&isDir];
if (!exists) {
[[NSFileManager defaultManager] createDirectoryAtPath: tmpFullPath
withIntermediateDirectories:YES attributes:nil error:nil];
}
return tmpFullPath;
}
Then, added a new bool property called cached, to set whether to use tmp or cache, if using cached, developers should manually call ImagePicker.clean(). It would be the quickest solution, instead of re-writing the main logic.
the reason for tmp files is because if reading directly from original file path you will get permission error. Creating tmp files where app has access is a workaround to make it possible to read files from react-native app. If you know solution to this problem, please let me know.
Yeah, that's correct. If you want to access the assets, like writing, you have to copy it from the camera roll first. However, when user do not want to use cropping or compression, all I need is just get the Asset-Library://.... hashed-path, then it is no need to make the tmp files.
And in my scene, I need to select photos and upload them, but not immediately, maybe after a few days or a few hours. If the only option is to use tmpDir, then all selected photos will be desctroyed by the System. Instead, we should provide an option for developer to choose whether to use tmpDir or cacheDir. @ivpusic
even for existing images when trying to read it I was getting permission error. Were you able to read it directly from link when you provide it to Image component or try to upload it via FormData?
@ivpusic I didn't use PhotoKit, I am using AlAssetLibrary to get the AlAsset object. I ensure the PhotoKit has a similar way to get this kind of url. The file with prefix Asset-Library:// will give you read and copy permission.
Here shows how to get the asset url with prefix Asset-Library://
ALAssetRepresentation* representation = [asset defaultRepresentation];
//this url has prefix => Asset-Library://
NSURL* url = [representation url];
Here shows how to deal with the file with prefix Asset-Library://
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
NSURL *assetLibraryURL = [[NSURL alloc] initWithString:filepath];
[library assetForURL:assetLibraryURL
resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *representation = [asset defaultRepresentation];
//get the full resolution image bitmap binary
UIImage *image = [UIImage imageWithCGImage:[representation fullResolutionImage]];
//return the binary data of the Image
callback(UIImageJPEGRepresentation(image, 1.0));
}
failureBlock:^(NSError *error) {
NSLog(@"ALAssetsLibrary assetForURL error: %@", error);
}];
See, there is no need to create any tmp files. I believe this could bring a better performance.
The simplest example is the CameraRoll component provided by react-native. That component always return AssetLibrary:// filepath. Above I just show how to deal with this kind of url.
http://facebook.github.io/react-native/releases/0.46/docs/cameraroll.html#cameraroll
thanks for the explanation. A lot of people are already using the library and relying on a current way how things work. Probably good start would be to return something like originalPath in the response, and then later I can take some time to refactor it, and make some breaking changes. Do you have some time to submit PR to return originalPath in the response? Thanks
Yeah, my pleasure. My suggestion is to implement this in three steps.
Man, You have done a great work! And I am very interested in making these changes.
I will take a look into the these three features, and create three different featured branchs to work on it. If everything works fine, do you want me to submit only one PR or three different PRs?
Awesome. Let's make it three. Easier to test, easier to review.
Hi, after digging into the PhotoKit Library, I think it's huge differences comparing to AlAssetLibrary. In the AlAssetLibrary, all images fetched from CameraRoll should be presented as a unique URL which has prefix: asset-library://106E99A1-4F6A-45A2-B320-B0AD4A8E8473. However, the PhotoKit uses a different thing called localIdentifier, which looks like this 106E99A1-4F6A-45A2-B320-B0AD4A8E8473/L0/001.
As the originalPath with prefix file:/// doesn't work on real devices but only simulator. On devices, it will get permission error like you said before.
In fact, in StackOverflow, some people figure out a quick hack to convert localidentifier to a assetURL https://stackoverflow.com/questions/28887638/how-to-get-an-alasset-url-from-a-phasset, but it might not be a good solution.
/**
* I write a litte util method to convert localIdentifier to assetURL in JavaScript
* @param localIdentifier looks like 91B1C271-C617-49CE-A074-E391BA7F843F/L0/001
* @param ext the extension: JPG, PNG, MOV
* @returns {string}
*/
export const convertLocalIdentifierToAssetLibrary = (localIdentifier, ext) => {
const hash = localIdentifier.split('/')[0];
return `assets-library://asset/asset.${ext}?id=${hash}&ext=${ext}`;
};
However, apple provides a new way to read the asset from CameraRoll, it's called fetchAssetsWithLocalIdentifiers:options:
In a word, I suggest what about we rename the originalPath to localIdentifier, it will be looked like 106E99A1-4F6A-45A2-B320-B0AD4A8E8473/L0/001
tnx for investigating. your PR is merged.
@gameboyVito 's approach worked perfectly for me. My returned path from saveToCameraRoll() was massaged to the ouptut string eventually: "assets-library://asset/asset.MP4?id=60F75F00-0307-645E-875D-B0927CE2BB7D&ext=MP4".
Most helpful comment
Hi, after digging into the PhotoKit Library, I think it's huge differences comparing to AlAssetLibrary. In the AlAssetLibrary, all images fetched from CameraRoll should be presented as a unique URL which has prefix:
asset-library://106E99A1-4F6A-45A2-B320-B0AD4A8E8473. However, the PhotoKit uses a different thing called localIdentifier, which looks like this106E99A1-4F6A-45A2-B320-B0AD4A8E8473/L0/001.As the originalPath with prefix
file:///doesn't work on real devices but only simulator. On devices, it will get permission error like you said before.In fact, in StackOverflow, some people figure out a quick hack to convert localidentifier to a assetURL https://stackoverflow.com/questions/28887638/how-to-get-an-alasset-url-from-a-phasset, but it might not be a good solution.
However, apple provides a new way to read the asset from CameraRoll, it's called
fetchAssetsWithLocalIdentifiers:options:In a word, I suggest what about we rename the
originalPathtolocalIdentifier, it will be looked like106E99A1-4F6A-45A2-B320-B0AD4A8E8473/L0/001