aws-sdk-android icon indicating copy to clipboard operation
aws-sdk-android copied to clipboard

Upload multiple files using TransferUtility

Open ersps25 opened this issue 6 years ago • 12 comments

My requirement is to upload multiple images using AWS-SDK-ANDROID, I am using TransferUtility, but I didn't find any method to upload multiple images inside documentation of TransferUtility. The only suggestion I went through on stackoverflow was to create a loop and upload multiple images, it doesn't seem me right way to do this task. Please suggest which approach should I follow to upload multiple images.

ersps25 avatar Aug 04 '18 17:08 ersps25

@ersps25 Thank you for reporting to us. This is a limitation of the TransferUtility. We will take this as a feature request to the team. At this time, please loop through the images and upload each individually.

mutablealligator avatar Aug 07 '18 16:08 mutablealligator

How soon can we expect this feature?

samyakjain avatar Oct 09 '18 10:10 samyakjain

Is it possible now?

ShwetaChauhan18 avatar Jan 29 '19 13:01 ShwetaChauhan18

now it is possible or not..?

vishalsgithub avatar May 08 '19 07:05 vishalsgithub

even I'm facing a similar issue, do we have any update on this?

ranjith-zen3 avatar Jan 03 '20 16:01 ranjith-zen3

Is it possible now ?

Suyash171 avatar Apr 07 '20 06:04 Suyash171

Thanks for your comments! No, we've not as of yet built this feature, but please continue to vote on it via reactions so we can properly prioritize it. For now, you should be able to upload multiple files by iterating through them (which is effectively what we'd do anyway if we were to build this feature) as suggesting in the initial issue.

jpignata avatar Apr 07 '20 11:04 jpignata

We will expose functionality for this, in the APIs, in the future.

For now, you could do something like this.

Firstly, some boiler-plate to obtain a TransferUtility instance:

Single<TransferUtility> transferUtility() {
    AWSConfiguration configuration = new AWSConfiguration(getApplicationContext());
    return s3Client(configuration).map(s3 ->
        TransferUtility.builder()
            .context(getApplicationContext())
            .s3Client(s3)
            .awsConfiguration(configuration)
            .build()
    );
}

Single<AmazonS3Client> s3Client(AWSConfiguration configuration) {
    return credentialsProvider().map(credentialsProvider -> {
        String regionInConfig = configuration
            .optJsonObject("S3TransferUtility")
            .getString("Region");
        Region region = Region.getRegion(regionInConfig);
        return new AmazonS3Client(credentialsProvider, region);
    });
}

Single<AWSMobileClient> credentialsProvider() {
    return Single.create(emitter -> {
        AWSMobileClient.getInstance().initialize(getApplicationContext(), new Callback<UserStateDetails>() {
            @Override
            public void onResult(UserStateDetails result) {
                emitter.onSuccess(AWSMobileClient.getInstance());
            }

            @Override
            public void onError(Exception error) {
                emitter.onError(error);
            }
        });
    });
}

Having that, a utility to upload multiple files, to multiple keys, and await the result:

Completable uploadMultiple(Map<File, String> fileToKeyUploads) {
    return transferUtility()
        .flatMapCompletable(transferUtility ->
            Observable.fromIterable(fileToKeyUploads.entrySet())
                .flatMapCompletable(entry -> uploadSingle(transferUtility, entry.getKey(), entry.getValue()))
        );
}

Completable uploadSingle(TransferUtility transferUtility, File aLocalFile, String toRemoteKey) {
    return Completable.create(emitter -> {
        transferUtility.upload(toRemoteKey, aLocalFile).setTransferListener(new TransferListener() {
            @Override
            public void onStateChanged(int id, TransferState state) {
                if (TransferState.FAILED.equals(state)) {
                    emitter.onError(new Exception("Transfer state was FAILED."));
                } else if (TransferState.COMPLETED.equals(state)) {
                    emitter.onComplete();
                }
            }

            @Override
            public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
            }

            @Override
            public void onError(int id, Exception exception) {
                emitter.onError(exception);
            }
        });
    });
}

Finally, use it:

private void example() {
    uploadMultiple(Collections.singletonMap("remote_key", new File()))
        .subscribeOn(Schedulers.io())
        .observeOn(Schedulers.io())
        .subscribe(() -> "Uploads completed!");
}

You could adapt it so that uploadMultiple ran the uploadSingle Completables in parallel.

jamesonwilliams avatar Apr 10 '20 19:04 jamesonwilliams

@jamesonwilliams As per the solution proposed by you, how do I map multiple file uploading progress change? Is it viable to take out an average of all the files?

gaurav-likeminds avatar Jul 22 '21 14:07 gaurav-likeminds

@gaurav-likeminds i also need such functionality. i created issue https://github.com/aws-amplify/aws-sdk-android/issues/2390 but since creation didn't get any answer

lon9man avatar Jul 22 '21 15:07 lon9man

@lon9man Right now I am using average of the progress of all the files getting uploaded. You can try to do that, it will give you an approx progress of the multi files as a whole.

gaurav-likeminds avatar Jul 22 '21 15:07 gaurav-likeminds

Hey @gaurav-likeminds and @lon9man, I implemented the code provided by @jamesonwilliams in June, last year and it works with map. Your comment's notification reminded me, so, here's the Git repo I had created for this to share with others. Feel free to use it.

MultiUploaderS3 - Upload/Download multiple files - AWS

Fauzdar1 avatar Jul 22 '21 16:07 Fauzdar1