I am using Firebase APIs on Android (SDK 34) to download files from my storage bucket to my shared Download folder. However, when I call StorageReference.getFile(Uri uri)
, it fails after creating a 0 byte file. However, if I use getBytes
and write the bytes to the file myself, it works fine.
Here's the code that does not work:
// This does not. See the exception details below
public void download(String remotePath, Uri localUri) {
StorageReference fileRef = storageRef.child(remotePath);
fileRef
.getFile(localUri)
.addOnFailureListener(
error -> {
System.out.println(error.getMessage());
});
}
The error message and stack trace:
error = {StorageException@26984} "com.google.firebase.storage.StorageException: An unknown error occurred, please check the HTTP result code and inner exception for server response."
cause = {IOException@26987} "java.io.IOException: No such file or directory"
backtrace = null
cause = {IOException@26987} "java.io.IOException: No such file or directory"
backtrace = null
cause = {IOException@26987} "java.io.IOException: No such file or directory"
detailMessage = "No such file or directory"
stackTrace = {StackTraceElement[14]@26998}
0 = {StackTraceElement@27003} "java.io.UnixFileSystem.createFileExclusively0(Native Method)"
1 = {StackTraceElement@27004} "java.io.UnixFileSystem.createFileExclusively(UnixFileSystem.java:349)"
2 = {StackTraceElement@27005} "java.io.File.createNewFile(File.java:1006)"
3 = {StackTraceElement@27006} "com.google.firebase.storage.FileDownloadTask.processResponse(FileDownloadTask.java:136)"
4 = {StackTraceElement@27007} "com.google.firebase.storage.FileDownloadTask.run(FileDownloadTask.java:228)"
5 = {StackTraceElement@27008} "com.google.firebase.storage.StorageTask.lambda$getRunnable$7$com-google-firebase-storage-StorageTask(StorageTask.java:1077)"
6 = {StackTraceElement@27009} "com.google.firebase.storage.StorageTask$$ExternalSyntheticLambda7.run(Unknown Source:2)"
7 = {StackTraceElement@27010} "com.google.firebase.concurrent.LimitedConcurrencyExecutor.lambda$decorate$0$com-google-firebase-concurrent-LimitedConcurrencyExecutor(LimitedConcurrencyExecutor.java:65)"
8 = {StackTraceElement@27011} "com.google.firebase.concurrent.LimitedConcurrencyExecutor$$ExternalSyntheticLambda0.run(Unknown Source:4)"
9 = {StackTraceElement@27012} "java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)"
10 = {StackTraceElement@27013} "java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)"
11 = {StackTraceElement@27014} "com.google.firebase.concurrent.CustomThreadFactory.lambda$newThread$0$com-google-firebase-concurrent-CustomThreadFactory(CustomThreadFactory.java:47)"
12 = {StackTraceElement@27015} "com.google.firebase.concurrent.CustomThreadFactory$$ExternalSyntheticLambda0.run(Unknown Source:4)"
13 = {StackTraceElement@27016} "java.lang.Thread.run(Thread.java:1012)"
However, this code works:
public void download(String remotePath, Uri localUri) {
StorageReference fileRef = storageRef.child(remotePath);
fileRef
.getBytes(1024 * 1024 * 16)
.addOnSuccessListener(
result -> {
try {
ContentResolver resolver = context.getContentResolver();
OutputStream stream = resolver.openOutputStream(localUri);
stream.write(result);
stream.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
});
}
And this is how I came created the localUri
:
ContentResolver resolver = context.getContentResolver();
String fileName = UUID.randomUUID().toString().toUpperCase();
Uri imagesUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
Uri localUri = resolver.insert(imagesUri, values);
Any ideas what's going wrong here? Thanks!
fileRef.getFile(uri) attempts to download a remote file from Storage to the local file URI that was passed to it. It must be a specific file path, as the documentation states (emphasis mine):
Asynchronously downloads the object at this StorageReference to a specified system filepath.
@NonNull Uri destinationUri
A file system URI representing the path the object should be downloaded to.
But you passed it a URI to a ContentProvider, and that's not supported. The SDK won't try to download the file and put it in the ContentProvider. The URI must identify a file. It's probably easier for you to just pass a File parameter instead, and make sure it's pointing to a location that's writable by the app, such as one of its internal storage locations.