Reputation: 1719
I am trying to upload 4 different images to firebase storage, my code works with a single image, but whenever I try to upload multiple images I get
E/StorageException: StorageException has occurred.
Object does not exist at location.
Code: -13010 HttpResult: 404
2021-11-18 22:45:16.584 com.example.test E/StorageException: { "error": { "code": 404, "message": "Not Found." }}
java.io.IOException: { "error": { "code": 404, "message": "Not Found." }}
at com.google.firebase.storage.network.NetworkRequest.parseResponse(NetworkRequest.java:445)
at com.google.firebase.storage.network.NetworkRequest.parseErrorResponse(NetworkRequest.java:462)
at com.google.firebase.storage.network.NetworkRequest.processResponseStream(NetworkRequest.java:453)
at com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:272)
at com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:289)
at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:76)
at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:68)
at com.google.firebase.storage.GetDownloadUrlTask.run(GetDownloadUrlTask.java:77)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
This is my code to upload images to firebase storage and even after getting errors, I can see the images on firebase Storage but I am getting the error on the app.
private synchronized void UploadToFireBaseStorage(ArrayList<String> filePath,int type) {
final ArrayList<String> multipleImages = new ArrayList<>();
Log.d(TAG,"Size of File at Upload Method "+filePath.size());
if (filePath.size()==0){
return;
}
Uri fileUri = null;
for (String s : filePath) {
if (s.contains(".jpg")) {
fileUri = Uri.fromFile(new File(s));
} else {
fileUri = Uri.parse(s);
}
StorageReference mStorageReference = FirebaseStorage.getInstance().getReference();
Log.d(TAG, "UploadToFireBaseStorage: StorageRef "+mStorageReference);
storageReference = mStorageReference.child("Photos").child(fileUri.getLastPathSegment());
Log.d(TAG, "UploadToFireBaseStorage: Storage Reference "+storageReference);
Log.d(TAG, "UploadToFireBaseStorage: File URI = "+fileUri);
String finalAttachmentType = attachmentType;
String finalUploadFolder = uploadFolder;
storageReference.putFile(fileUri).addOnSuccessListener(this, new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
storageReference.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess(@NonNull Uri uri) {
Log.d(TAG, "Image Uploaded ");
Log.d(TAG, "onSuccess: URI Uploaded == "+uri);
}
});
}
}).addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
@Override
public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
double progress = 100.0 * taskSnapshot.getBytesTransferred() / (double) taskSnapshot.getTotalByteCount();
Log.d(TAG, "Upload is " + progress + "% done");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.d(TAG, "onFailure: "+s);
}
});
}
Log.d(TAG,"Image List "+multipleImages.toString());
}
These are the storage reference i got for 4 different images
Storage Reference gs://example-89902.appspot.com/Photos/Image%20IMG_1637256617899.jpg
Storage Reference gs://example-89902.appspot.com/Photos/Image%20IMG_1637256617949.jpg
Storage Reference gs://example-89902.appspot.com/Photos/Image%20IMG_1637256618132.jpg
Storage Reference gs://example-89902.appspot.com/Photos/Image%20IMG_1637256618070.jpg
Firebase Storage Rules:
Firebase Storage Rules
service firebase.storage {
match /b/bucket/o {
match /{allPaths=**} {
allow read, write;
}
}
}
Upvotes: 4
Views: 931
Reputation: 53381
According to the stack trace the problem seems to be related to obtaining the download url:
at com.google.firebase.storage.GetDownloadUrlTask.run(GetDownloadUrlTask.java:77)
It's probably caused by this code fragment:
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
storageReference.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess(@NonNull Uri uri) {
Log.d(TAG, "Image Uploaded ");
Log.d(TAG, "onSuccess: URI Uploaded == "+uri);
}
});
}
My guess is that there is some kind of race condition between the upload success callback and the instant in which the storage reference and the corresponding download url are available.
As suggested in the Firebase documentation, please, try modifying your code like this in order to obtain the download url:
private synchronized void UploadToFireBaseStorage(ArrayList<String> filePath,int type) {
final ArrayList<String> multipleImages = new ArrayList<>();
Log.d(TAG,"Size of File at Upload Method "+filePath.size());
if (filePath.size()==0){
return;
}
Uri fileUri = null;
for (String s : filePath) {
if (s.contains(".jpg")) {
fileUri = Uri.fromFile(new File(s));
} else {
fileUri = Uri.parse(s);
}
StorageReference mStorageReference = FirebaseStorage.getInstance().getReference();
Log.d(TAG, "UploadToFireBaseStorage: StorageRef "+mStorageReference);
storageReference = mStorageReference.child("Photos").child(fileUri.getLastPathSegment());
Log.d(TAG, "UploadToFireBaseStorage: Storage Reference "+storageReference);
Log.d(TAG, "UploadToFireBaseStorage: File URI = "+fileUri);
String finalAttachmentType = attachmentType;
String finalUploadFolder = uploadFolder;
UploadTask uploadTask = storageReference.putFile(fileUri).addOnSuccessListener(this, new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
// The stuff you considerd appropriate
Log.d(TAG, "Image Uploaded ");
}
}).addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
@Override
public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
double progress = 100.0 * taskSnapshot.getBytesTransferred() / (double) taskSnapshot.getTotalByteCount();
Log.d(TAG, "Upload is " + progress + "% done");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.d(TAG, "onFailure: "+s);
}
});
uploadTask.continueWithTask(new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() {
@Override
public Task<Uri> then(@NonNull Task<UploadTask.TaskSnapshot> task) throws Exception {
if (!task.isSuccessful()) {
throw task.getException();
}
// Continue with the task to get the download URL
return ref.getDownloadUrl();
}
}).addOnCompleteListener(new OnCompleteListener<Uri>() {
@Override
public void onComplete(@NonNull Task<Uri> task) {
if (task.isSuccessful()) {
Uri downloadUri = task.getResult();
Log.d(TAG, "onSuccess: URI Uploaded == "+downloadUri);
} else {
// Handle failures
// ...
}
}
});
}
Log.d(TAG,"Image List "+multipleImages.toString());
}
Previously UploadTask.TaskSnapshot
had a getDownloadUrl()
method, but it is no longer available. The subject has been discussed in different issues like this one, for instance, which provides further alternatives as well.
As a side note, and probably unrelated, but certainly curious, you pointed out in your comments that two of the images are uploaded successfully and two not. As you can see in the source code of Uploadtask
, this kind of tasks are scheduled using the following code:
@Override
protected void schedule() {
StorageTaskScheduler.getInstance().scheduleUpload(getRunnable());
}
StorageTaskScheduler
schedules uploads in a thread pool executor of a maximum of two threads:
private static final ThreadPoolExecutor UPLOAD_QUEUE_EXECUTOR =
new ThreadPoolExecutor(
2, 2, 5, TimeUnit.SECONDS, mUploadQueue, new StorageThreadFactory("Upload-"));
Maybe some uploads are being blocked by this thread pool as you are trying uploading a greater number of images that the available number of threads.
In contrast, the operation for obtaining the download url is performed in a different thread pool:
private static final ThreadPoolExecutor COMMAND_POOL_EXECUTOR =
new ThreadPoolExecutor(
5, 5, 5, TimeUnit.SECONDS, mCommandQueue, new StorageThreadFactory("Command-"));
As you can see, in this pool the number of threads is five and it means there is enough room to submit the get download task operations.
I reviewed the code and cannot find any leak, but it is certain that this difference may be related to your problem in any way.
Upvotes: 1