Retrieving image from Firebase Storage

Viewed 1252

I know this is a discussed topic, but here I am, after all other solutions I've found did not solve my problem. I saved multiple images in Firebase Storage and now I want to get one of them and put it into an ImageView on an AlertDialog (or another Activity).

I've read about Glide solution, but it doesn't work for me. Gilde:

Glide.with(ViewScenesSG.this) //ViewScenesSG is the Activity
                .load(reference) //the storage reference
                .into(imageView); //my imageView

Dependencies for Glide (I've tried with multiple versions and 4.8.0 is the only one that doesn't give me Build error):

implementation 'com.github.bumptech.glide:glide:4.8.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
annotationProcessor 'androidx.annotation:annotation:1.1.0'

I've also read about the .using(new FirebaseImageLoader()) method, I've implemented the FirebaseImageLoader(), but the using() method is not found => build error. I believe it's been removed.

The second version I've tried is this one (found in another question here):

final long ONE_MEGABYTE = 1024 * 1024;
       reference.getBytes(ONE_MEGABYTE)
           .addOnSuccessListener(new OnSuccessListener<byte[]>() {
               @Override
               public void onSuccess(byte[] bytes) {
                   Bitmap bm = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                   DisplayMetrics dm = new DisplayMetrics();
                   getWindowManager().getDefaultDisplay().getMetrics(dm);
                   
                   imageView.setMinimumHeight(dm.heightPixels);
                   imageView.setMinimumWidth(dm.widthPixels);
                   imageView.setImageBitmap(bm);
               }
           }).addOnFailureListener(new OnFailureListener() {
           @Override
           public void onFailure(@NonNull Exception exception) {
               Toast.makeText(ViewScenesSG.this, "Couldn't retrieve image.", Toast.LENGTH_SHORT).show();
           }
       });

But every time it goes on the Failure listener. Therefore, my imageView always remains empty.

In my logcat I observed this error:

E/StorageException: Could not open resulting stream.
    java.io.IOException: Could not open resulting stream.
        at com.google.firebase.storage.StreamDownloadTask.createDownloadStream(com.google.firebase:firebase-storage@@19.1.1:145)
        at com.google.firebase.storage.StreamDownloadTask.access$000(com.google.firebase:firebase-storage@@19.1.1:36)
        at com.google.firebase.storage.StreamDownloadTask$1.call(com.google.firebase:firebase-storage@@19.1.1:167)
        at com.google.firebase.storage.StreamDownloadTask$1.call(com.google.firebase:firebase-storage@@19.1.1:164)
        at com.google.firebase.storage.StreamDownloadTask$StreamProgressWrapper.ensureStream(com.google.firebase:firebase-storage@@19.1.1:325)
        at com.google.firebase.storage.StreamDownloadTask$StreamProgressWrapper.access$100(com.google.firebase:firebase-storage@@19.1.1:262)
        at com.google.firebase.storage.StreamDownloadTask.run(com.google.firebase:firebase-storage@@19.1.1:175)
        at com.google.firebase.storage.StorageTask.lambda$getRunnable$7(com.google.firebase:firebase-storage@@19.1.1:1072)
        at com.google.firebase.storage.StorageTask$$Lambda$12.run(Unknown Source:2)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        at java.lang.Thread.run(Thread.java:764)
E/StorageException: StorageException has occurred.
    Object does not exist at location.
     Code: -13010 HttpResult: 404

But the image is there, in the Storage, and the reference points directly to it:

storageRef = storage.getReference("photos").child(title); //for all images in the current folder named with the 'title' attribute
StorageReference reference = storageRef.child(imageName); //for the image I want to retrieve

What am I missing?

EDIT: Using the suggestion in the coments, I tried with:

reference.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
            @Override
            public void onSuccess(Uri uri) {
                Glide.with(ViewScenesSG.this)
                        .load(uri.toString())
                        .into(imageView);
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                Toast.makeText(ViewScenesSG.this, "Retrieving image failed",
                        Toast.LENGTH_SHORT).show();
            }
        });

but this time I get the error:

E/StorageException: {  "error": {    "code": 404,    "message": "Not Found.  Could not get object",    "status": "GET_OBJECT"  }}
    java.io.IOException: {  "error": {    "code": 404,    "message": "Not Found.  Could not get object",    "status": "GET_OBJECT"  }}
        at com.google.firebase.storage.network.NetworkRequest.parseResponse(com.google.firebase:firebase-storage@@19.1.1:433)
        at com.google.firebase.storage.network.NetworkRequest.parseErrorResponse(com.google.firebase:firebase-storage@@19.1.1:450)
        at com.google.firebase.storage.network.NetworkRequest.processResponseStream(com.google.firebase:firebase-storage@@19.1.1:441)
        at com.google.firebase.storage.network.NetworkRequest.performRequest(com.google.firebase:firebase-storage@@19.1.1:272)
        at com.google.firebase.storage.network.NetworkRequest.performRequest(com.google.firebase:firebase-storage@@19.1.1:286)
        at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(com.google.firebase:firebase-storage@@19.1.1:70)
        at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(com.google.firebase:firebase-storage@@19.1.1:62)
        at com.google.firebase.storage.GetDownloadUrlTask.run(com.google.firebase:firebase-storage@@19.1.1:76)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
        at java.lang.Thread.run(Thread.java:764)
2 Answers

I found the following solution (as suggested in the comments before):

Using the Firebase UI plugin with Gilde, I added the following dependency:

implementation 'com.firebaseui:firebase-ui-storage:6.2.0'

Now, having the StorageReference reference pointing to the image, and the ImageView in which the content must be uploaded, the following code retrieves the image and puts it in the ImageView:

Glide.with(ViewScenesSG.this)  //this is the current Activity
                .load(reference)
                .into(imageView);

To be mentioned that the ImageView must have the dimensions set to a specific value, not using wrap_content or match_parent (at least for height in my case), otherwise it will appear as a small icon. This can also depend on how the image was saved before in the Storage.

Hope this will help someone else as well!

I'm not sure what's going wrong in your second snippet, but in the first snippet you seem to be passing a StorageReference to Glide. Since Glide doesn't know anything about Firebase Storage, that won't work.

Instead, you should get a download URL from Firebase Storage, which is a regular HTTPS URL that provides public access to the data, and pass that to Glide to render in the view.

Something like:

reference.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
    @Override
    public void onSuccess(Uri uri) {
        // Got the download URL for 'users/me/profile.png'

        Glide.with(ViewScenesSG.this)
                .load(uri.toString())
                .into(imageView);
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception exception) {
        // Handle any errors
    }
});
Related