Open mitchdowney opened 2 years ago
@mitchdowney yes it is possible.
copyfile takes three arguments uri of source uri of destination and callback
make sure you provide uri only not path . and the uri should not be document tree selected uri
Update to 1.9.3. it supports reading and writing to normal paths such as cache and data directory.
@ambessh hmm I have 1.9.3 installed, and I'm not sure what I'm doing wrong then...
When I call this:
const result = await ScopedStorage.stat('file://data/user/0/com.podverse/files/_qhiTuJQT.mp3')
I get:
Error: 'file://data/user/0/com.podverse/files/_qhiTuJQT.mp3'does not have permission to read/write
Is that an invalid uri? (that is the RNFS.DocumentDirectoryPath btw)
Also, the copyFile call I am attempting is:
await ScopedStorage.copyFile(`file://data/user/0/com.podverse/files/6NvramAVN.mp3`, content://com.android.externalstorage.documents/tree/01F6-AC3F%3APodcasts/document/01F6-AC3F%3APodcasts/6NvramAVN.mp3, () => {})
That doesn't throw an error, but the file doesn't get saved to the SD card either...is there anything you can see I'm doing wrong? I'm not using a callback, but I'm not sure I need to?
@ambessh hmm I have 1.9.3 installed, and I'm not sure what I'm doing wrong then...
When I call this:
const result = await ScopedStorage.stat('file://data/user/0/com.podverse/files/_qhiTuJQT.mp3')
I get:
Error: 'file://data/user/0/com.podverse/files/_qhiTuJQT.mp3'does not have permission to read/write
Is that an invalid uri? (that is the RNFS.DocumentDirectoryPath btw)
Also, the copyFile call I am attempting is:
await ScopedStorage.copyFile(`file://data/user/0/com.podverse/files/6NvramAVN.mp3`, content://com.android.externalstorage.documents/tree/01F6-AC3F%3APodcasts/document/01F6-AC3F%3APodcasts/6NvramAVN.mp3, () => {})
That doesn't throw an error, but the file doesn't get saved to the SD card either...is there anything you can see I'm doing wrong? I'm not using a callback, but I'm not sure I need to?
+1
@mitchdowney during copy file i had the same issue . it was not saving it and the callback was returning nothing. Go to -> node modules/react-native-scoped-storage/android/src/main/java/com/amaarahmed/scopedstorage/RNScopedStorageModule.java search copyFile function in that java file and replace with this
@ReactMethod
public void copyFile(String path, String dest, Callback callback) {
String pathToUri = Uri.fromFile(new File(path)).toString();
AsyncTask.execute(() -> {
InputStream in = null;
OutputStream out = null;
String message = "";
try {
if (!exists(pathToUri)) {
message = "Source file does not exist";
callback.invoke((message));
return;
}
ParcelFileDescriptor inputDescriptor = reactContext.getContentResolver().openFileDescriptor(Uri.parse(pathToUri), "rw");
in = new FileInputStream(inputDescriptor.getFileDescriptor());
if (!exists(dest)) {
message = "Destination file does not exist. Please create destination file with createFile.";
callback.invoke((message));
return;
}
ParcelFileDescriptor outputDescriptor = reactContext.getContentResolver().openFileDescriptor(Uri.parse(dest), "rw");
out = new FileOutputStream(outputDescriptor.getFileDescriptor());
byte[] buf = new byte[10240];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
} catch (Exception err) {
message += err.getLocalizedMessage();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (Exception e) {
message += e.getLocalizedMessage();
}
}
if (message != "") {
callback.invoke(message);
} else {
callback.invoke();
}
});
}
and now in react javascript dont pass uri in the first argument of copyfile function . just pass normal path . It will work. laters you can create a patch of the package.
@ammarahm-ed I tried your code, updated RNScopedStorageModule.java (verified the changes are used in the react native app), then call this:
await ScopedStorage.copyFile("/storage/emulated/0/Download/lwx0wMYSjP.mp3", "content://com.android.externalstorage.documents/tree/01F6-AC3F%3APodverse/document/01F6-AC3F%3APodverse/lwx0wMYSjP.mp3", (msg) => {
console.log('ScopedStorage.copyFile msg', msg)
})
But then I get:
ScopedStorage.copyFile msg Failed to open for writing: java.io.FileNotFoundException: open failed: EISDIR (Is a directory)
Any ideas what the "EISDIR is a directory" is about? That path and uri does not seem like a directory to me...
I also tried this:
await ScopedStorage.createFile("content://com.android.externalstorage.documents/tree/01F6-AC3F%3APodverse/document/01F6-AC3F%3APodverse/", `${episode.id}${ext}`, 'audio/mpeg')
await ScopedStorage.copyFile("/storage/emulated/0/Download/lwx0wMYSjP.mp3", "content://com.android.externalstorage.documents/tree/01F6-AC3F%3APodverse/document/01F6-AC3F%3APodverse/lwx0wMYSjP.mp3", (msg) => {
console.log('ScopedStorage.copyFile msg', msg)
})
And I verified the file is created as expected...but I still get the "is a directory" error.
@ammarahm-ed eureka! copyFile
works for me is I use the uri:
content://com.android.externalstorage.documents/tree/01F6-AC3F%3APodverse/document/01F6-AC3F%3APodverse%2Flwx0wMYSjP.mp3
With the URL encoded %2F
character instead of /
. Not sure why it's necessary...but it's finally saving to the SD card! Thank you for your help 🙏
@ammarahm-ed ahhh...but there's a bug with this...
It seems like the %2F sometimes works, and sometimes throws a "ENOENT No such file or directory" error:
Works:
content://com.android.externalstorage.documents/tree/01F6-AC3F%3APodcasts/document/01F6-AC3F%3APodcasts%2Fesgi0q1Pm.mp3 content://com.android.externalstorage.documents/tree/01F6-AC3F%3APodcasts/document/01F6-AC3F%3APodcasts%2FuJbRmLuc0.mp3
Fails with ENOENT error:
content://com.android.externalstorage.documents/tree/01F6-AC3F%3APodcasts/document/01F6-AC3F%3APodcasts%2FGwHcavR3a.mp3 content://com.android.externalstorage.documents/tree/01F6-AC3F%3APodcasts/document/01F6-AC3F%3APodcasts%2F6gWMTVqam.mp3
I guess there is some kind of URL encoded clash, depending on the first character of the file id?
@mitchdowney pls dont hardcode it . Get uri from when you create the file .
@ambessh I was getting very buggy and inconsistent results with this...but I think it may have been conflicts caused by identical files already existing in the temporary DocumentDirectoryPath from previous test downloads.
After wiping the app completely and deleting all data, this code seems to work consistently when run in the react-native-background-downloader done
method:
if (customLocation) {
const tempDownloadFileType = await ScopedStorage.stat(origDestination)
const newFileType = await ScopedStorage.createFile(customLocation, `${episode.id}${ext}`, 'audio/mpeg')
if (tempDownloadFileType && newFileType) {
const { uri: tempFileUri } = tempDownloadFileType
const { uri: newFileUri } = newFileType
await ScopedStorage.copyFile(tempFileUri, newFileUri, (msg) => {
console.log('ScopedStorage.copyFile msg', msg)
})
await ScopedStorage.deleteFile(tempFileUri)
}
}
HOWEVER, stat
throws a "cannot read/write" permissions error. I had to go into the RNScopedStorageModule and modify the hasPermission function so that it always returns true
. This is obviously no good...but why is read/write permission even needed for the DocumentDirectoryPath? Shouldn't it only be "read" permission that is needed? And isn't this a path that we should be able to read/write to by default anyway, even with scoped storage?
It appears the problem is that DocumentDirectoryPath is not returned by the getPersistedUriPermissions
function. It seems crazy though if I have to tell the user to somehow manually allow writes to the DocumentDirectoryPath.
ALSO: deleteFile
is failing. It says:
Error: Invalid URI: /storage/emulated/0/Android/data/com.podverse/files/M6FsE-t6d.mp3
What's odd to me is this is the URI that is returned by the stat
function...so something seems wrong there.
@ammarahm-ed eureka!
copyFile
works for me is I use the uri:
content://com.android.externalstorage.documents/tree/01F6-AC3F%3APodverse/document/01F6-AC3F%3APodverse%2Flwx0wMYSjP.mp3
With the URL encoded
%2F
character instead of/
. Not sure why it's necessary...but it's finally saving to the SD card! Thank you for your help 🙏
hey bro , i am facing issue "source file not exist" and i used this uri : 'content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fmedia%2Fcom.whatsapp%2FWhatsApp%2FMedia%2F.Statuses/document/primary%3AAndroid%2Fmedia%2Fcom.whatsapp%2FWhatsApp%2FMedia%2F.Statuses%2F'
and this is my copy file function = const copyFileFunction = async (sourceUrl, destUrl) => { const fileStat = await ScopedStoragePackage.stat(sourceUrl) console.log({fileStat}); await ScopedStoragePackage.copyFile(fileStat.uri,destUrl,(res)=>{console.log({res});}) };
could you pls tell me where i am wrong ?
Hello, sorry as I should probably be able to figure this out on my own...but it is possible to call
copyFile
to move a file from a normal file storage directory, into a scoped storage directory?By "normal" file storage directory I mean files saved to the paths that react-native-fs provides:
Whenever I call
copyFile
using these paths (although I haven't tried all of them yet) I get a read/write permission error, such as:We're using react-native-background-downloader, and I haven't been able to figure out a way to get files downloaded with it into scoped storage...it seems like react-native-background-downloader library just won't work with
content://
as a destination, and react-native-scoped-storage can't read files from the paths I listed above...so it seems like we're stuck? No workarounds? Any info or advice would be appreciated.