inhaler
inhaler

Reputation: 425

Upload Multiple images and wait for completion before returning, android and firebase

Hello I am trying to upload multiple images, wait for them to return, compile the download uri's into an object and send it back to my activity. I am using this as reference for upload, firebase. So far i have this

 private void saveStepWithImages(@NonNull Step step, Callback callback){

    if(step.getStepId() == null){
       Collection<Image> images =  step.getImages().values();

        List<Task<Uri>> taskArrayList= new ArrayList<>();
        for (Image i: images) {
            taskArrayList.add(uploadImageTask(new ImageUtils().StringToBitMap(i.getImageUrl()), i.getImageReference()));
        }

        Tasks.whenAll(taskArrayList).addOnCompleteListener(task -> {
            Uri downloadUri = task.getResult(); // throws an error because task.getResult is void
        });

    }else{
        updateStepInFirebase(step, callback);
    }

}

and in my upload images

private Task<Uri> uploadImageTask(final Bitmap bitmap, String prepend){
    final StorageReference ref = mStorageRef.child( prepend );

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
    byte[] data = baos.toByteArray();

    UploadTask uploadTask = ref.putBytes(data);
    bitmap.recycle();

    return uploadTask.continueWithTask(task -> {
        bitmap.recycle();
        return ref.getDownloadUrl();
    });
}

Step is a custom object i created it contains a Map of images with a string as the key and the value being an image. My image class looks like this

public class Image implements Parcelable {

    private String imageUrl;
    private String imageReference;


    public void Image(){

    }

    //Setters and getters here;
}

Any suggestions would be really appreciated. Thanks!

Upvotes: 5

Views: 1616

Answers (2)

Giovanny Pi&#241;eros
Giovanny Pi&#241;eros

Reputation: 583

You can upload multiple files to firebase by nesting all the calls in one array, and adding each call to the Task API of firebase:

Define the reference and an array of tasks

StorageReference mStorageRef = FirebaseStorage.getInstance().getReference();

List<Task> myTasks = new ArrayList<>();

In this example im using a map that contains each file an its corresponding storage destination

    for (Map.Entry<String, Attachment> entry : storageRouteMap.entrySet()) {

        String path = entry.getKey();

        final Attachment localAtt = entry.getValue();
        Uri fileUri = localAtt.getMyUri();

I will put each task in the array of tasks, for a file i have three tasks, one for uploading the file, one for getting the url of the storage and one for writing the metadata in the real time database.

        final StorageReference ref = mStorageRef.child(path);
        ThreadPerTaskExecutor executor = new ThreadPerTaskExecutor();
        UploadTask t1 = ref.putFile(fileUri);

        myTasks.add(t1);

        Task<Uri> t2 = t1.continueWithTask(executor,new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() {
            @Override
            public Task<Uri> then(@NonNull Task<UploadTask.TaskSnapshot> task) throws Exception {
                if (!task.isSuccessful()) {
                    throw task.getException();
                }

                return ref.getDownloadUrl();
            }
        });

        myTasks.add(t2);

        Task<Void> t3 = t2.continueWithTask(executor,new Continuation<Uri, Task<Void>>() {
            @Override
            public Task<Void> then(@NonNull Task<Uri> task) throws Exception {
                if (!task.isSuccessful()) {
                    throw task.getException();
                }

                Attachment uploadAtt = new Attachment();
                uploadAtt.name = localAtt.name;
                uploadAtt.url = task.getResult().toString();
                uploadAtt.type = localAtt.type;
                String idAtt = UtilFirebase.getAttachmentReference().push().getKey();

                UtilLog.LogToConsole(TAG," => "+postId+" => "+uidAuthor+" =>"+idAtt);

                return UtilFirebase.getAttachmentReference()
                        .child(postId)
                        .child(uidAuthor)
                        .child(idAtt)
                        .setValue(uploadAtt);

            }
        }).continueWith(executor,new VideoTransaction(communityId,localAtt.size,localAtt.type));

        myTasks.add(t3);
    }

Finally i will see if all the tasks where completed or if there was an error, either way this will communicate the result to the main thread.

    Task finish = Tasks.whenAll((Collection) myTasks);

    finish.addOnCompleteListener(new ThreadPerTaskExecutor(), new OnCompleteListener() {
        @Override
        public void onComplete(@NonNull Task task) {
            if (task.isSuccessful()) {
                callback.onComplete();
            } else {
                callback.onError(task.getException().toString());
            }
        }
    });

Upvotes: 0

Alex Mamo
Alex Mamo

Reputation: 139029

The key for solving this problem is to use Tasks's whenAllSuccess() method:

Returns a Task with a list of Task results that completes successfully when all of the specified Tasks complete successfully.

Insted of Tasks's whenAll() method:

Returns a Task that completes successfully when all of the specified Tasks complete successfully.

Please see more informations about Tasks class.

Upvotes: 5

Related Questions