Skip to content

Commit 1f73dba

Browse files
Add upload InputStream API (#955)
* Add upload inputStream api * Cleanup code, add RxStorageBinding * fix indencing, copyright * chore(storage) eliminate several request classes by making one generic one (#967) * Address comments * Upgrade SDK version Co-authored-by: Richard McClellan <[email protected]>
1 parent 8ccb280 commit 1f73dba

26 files changed

+1435
-120
lines changed

aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/AWSS3StoragePlugin.java

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,28 +29,33 @@
2929
import com.amplifyframework.storage.operation.StorageListOperation;
3030
import com.amplifyframework.storage.operation.StorageRemoveOperation;
3131
import com.amplifyframework.storage.operation.StorageUploadFileOperation;
32+
import com.amplifyframework.storage.operation.StorageUploadInputStreamOperation;
3233
import com.amplifyframework.storage.options.StorageDownloadFileOptions;
3334
import com.amplifyframework.storage.options.StorageGetUrlOptions;
3435
import com.amplifyframework.storage.options.StorageListOptions;
3536
import com.amplifyframework.storage.options.StorageRemoveOptions;
3637
import com.amplifyframework.storage.options.StorageUploadFileOptions;
38+
import com.amplifyframework.storage.options.StorageUploadInputStreamOptions;
3739
import com.amplifyframework.storage.result.StorageDownloadFileResult;
3840
import com.amplifyframework.storage.result.StorageGetUrlResult;
3941
import com.amplifyframework.storage.result.StorageListResult;
4042
import com.amplifyframework.storage.result.StorageRemoveResult;
4143
import com.amplifyframework.storage.result.StorageTransferProgress;
4244
import com.amplifyframework.storage.result.StorageUploadFileResult;
45+
import com.amplifyframework.storage.result.StorageUploadInputStreamResult;
4346
import com.amplifyframework.storage.s3.operation.AWSS3StorageDownloadFileOperation;
4447
import com.amplifyframework.storage.s3.operation.AWSS3StorageGetPresignedUrlOperation;
4548
import com.amplifyframework.storage.s3.operation.AWSS3StorageListOperation;
4649
import com.amplifyframework.storage.s3.operation.AWSS3StorageRemoveOperation;
4750
import com.amplifyframework.storage.s3.operation.AWSS3StorageUploadFileOperation;
51+
import com.amplifyframework.storage.s3.operation.AWSS3StorageUploadInputStreamOperation;
4852
import com.amplifyframework.storage.s3.options.AWSS3StorageUploadFileOptions;
53+
import com.amplifyframework.storage.s3.options.AWSS3StorageUploadInputStreamOptions;
4954
import com.amplifyframework.storage.s3.request.AWSS3StorageDownloadFileRequest;
5055
import com.amplifyframework.storage.s3.request.AWSS3StorageGetPresignedUrlRequest;
5156
import com.amplifyframework.storage.s3.request.AWSS3StorageListRequest;
5257
import com.amplifyframework.storage.s3.request.AWSS3StorageRemoveRequest;
53-
import com.amplifyframework.storage.s3.request.AWSS3StorageUploadFileRequest;
58+
import com.amplifyframework.storage.s3.request.AWSS3StorageUploadRequest;
5459
import com.amplifyframework.storage.s3.service.AWSS3StorageService;
5560
import com.amplifyframework.storage.s3.service.StorageService;
5661

@@ -60,6 +65,7 @@
6065
import org.json.JSONObject;
6166

6267
import java.io.File;
68+
import java.io.InputStream;
6369
import java.util.concurrent.ExecutorService;
6470
import java.util.concurrent.Executors;
6571
import java.util.concurrent.TimeUnit;
@@ -307,7 +313,7 @@ public StorageUploadFileOperation<?> uploadFile(
307313
@NonNull Consumer<StorageUploadFileResult> onSuccess,
308314
@NonNull Consumer<StorageException> onError
309315
) {
310-
AWSS3StorageUploadFileRequest request = new AWSS3StorageUploadFileRequest(
316+
AWSS3StorageUploadRequest<File> request = new AWSS3StorageUploadRequest<>(
311317
key,
312318
local,
313319
options.getAccessLevel() != null
@@ -329,6 +335,62 @@ public StorageUploadFileOperation<?> uploadFile(
329335
return operation;
330336
}
331337

338+
@NonNull
339+
@Override
340+
public StorageUploadInputStreamOperation<?> uploadInputStream(
341+
@NonNull String key,
342+
@NonNull InputStream local,
343+
@NonNull Consumer<StorageUploadInputStreamResult> onSuccess,
344+
@NonNull Consumer<StorageException> onError
345+
) {
346+
StorageUploadInputStreamOptions options = StorageUploadInputStreamOptions.defaultInstance();
347+
return uploadInputStream(key, local, options, NoOpConsumer.create(), onSuccess, onError);
348+
}
349+
350+
@NonNull
351+
@Override
352+
public StorageUploadInputStreamOperation<?> uploadInputStream(
353+
@NonNull String key,
354+
@NonNull InputStream local,
355+
@NonNull StorageUploadInputStreamOptions options,
356+
@NonNull Consumer<StorageUploadInputStreamResult> onSuccess,
357+
@NonNull Consumer<StorageException> onError
358+
) {
359+
return uploadInputStream(key, local, options, NoOpConsumer.create(), onSuccess, onError);
360+
}
361+
362+
@NonNull
363+
@Override
364+
public StorageUploadInputStreamOperation<?> uploadInputStream(
365+
@NonNull String key,
366+
@NonNull InputStream local,
367+
@NonNull StorageUploadInputStreamOptions options,
368+
@NonNull Consumer<StorageTransferProgress> onProgress,
369+
@NonNull Consumer<StorageUploadInputStreamResult> onSuccess,
370+
@NonNull Consumer<StorageException> onError
371+
) {
372+
AWSS3StorageUploadRequest<InputStream> request = new AWSS3StorageUploadRequest<>(
373+
key,
374+
local,
375+
options.getAccessLevel() != null
376+
? options.getAccessLevel()
377+
: defaultAccessLevel,
378+
options.getTargetIdentityId(),
379+
options.getContentType(),
380+
options instanceof AWSS3StorageUploadInputStreamOptions
381+
? ((AWSS3StorageUploadInputStreamOptions) options).getServerSideEncryption()
382+
: ServerSideEncryption.NONE,
383+
options.getMetadata()
384+
);
385+
386+
AWSS3StorageUploadInputStreamOperation operation = new AWSS3StorageUploadInputStreamOperation(
387+
storageService, cognitoAuthProvider, request, onProgress, onSuccess, onError
388+
);
389+
operation.start();
390+
391+
return operation;
392+
}
393+
332394
@NonNull
333395
@Override
334396
public StorageRemoveOperation<?> remove(

aws-storage-s3/src/main/java/com/amplifyframework/storage/s3/operation/AWSS3StorageUploadFileOperation.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import com.amplifyframework.storage.result.StorageUploadFileResult;
3030
import com.amplifyframework.storage.s3.CognitoAuthProvider;
3131
import com.amplifyframework.storage.s3.ServerSideEncryption;
32-
import com.amplifyframework.storage.s3.request.AWSS3StorageUploadFileRequest;
32+
import com.amplifyframework.storage.s3.request.AWSS3StorageUploadRequest;
3333
import com.amplifyframework.storage.s3.service.StorageService;
3434
import com.amplifyframework.storage.s3.utils.S3Keys;
3535

@@ -44,7 +44,7 @@
4444
/**
4545
* An operation to upload a file from AWS S3.
4646
*/
47-
public final class AWSS3StorageUploadFileOperation extends StorageUploadFileOperation<AWSS3StorageUploadFileRequest> {
47+
public final class AWSS3StorageUploadFileOperation extends StorageUploadFileOperation<AWSS3StorageUploadRequest<File>> {
4848
private final StorageService storageService;
4949
private final CognitoAuthProvider cognitoAuthProvider;
5050
private final Consumer<StorageTransferProgress> onProgress;
@@ -64,7 +64,7 @@ public final class AWSS3StorageUploadFileOperation extends StorageUploadFileOper
6464
public AWSS3StorageUploadFileOperation(
6565
@NonNull StorageService storageService,
6666
@NonNull CognitoAuthProvider cognitoAuthProvider,
67-
@NonNull AWSS3StorageUploadFileRequest request,
67+
@NonNull AWSS3StorageUploadRequest<File> request,
6868
@NonNull Consumer<StorageTransferProgress> onProgress,
6969
@NonNull Consumer<StorageUploadFileResult> onSuccess,
7070
@NonNull Consumer<StorageException> onError
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package com.amplifyframework.storage.s3.operation;
17+
18+
import android.annotation.SuppressLint;
19+
import androidx.annotation.NonNull;
20+
21+
import com.amplifyframework.core.Amplify;
22+
import com.amplifyframework.core.Consumer;
23+
import com.amplifyframework.hub.HubChannel;
24+
import com.amplifyframework.hub.HubEvent;
25+
import com.amplifyframework.storage.StorageChannelEventName;
26+
import com.amplifyframework.storage.StorageException;
27+
import com.amplifyframework.storage.operation.StorageUploadInputStreamOperation;
28+
import com.amplifyframework.storage.result.StorageTransferProgress;
29+
import com.amplifyframework.storage.result.StorageUploadInputStreamResult;
30+
import com.amplifyframework.storage.s3.CognitoAuthProvider;
31+
import com.amplifyframework.storage.s3.ServerSideEncryption;
32+
import com.amplifyframework.storage.s3.request.AWSS3StorageUploadRequest;
33+
import com.amplifyframework.storage.s3.service.StorageService;
34+
import com.amplifyframework.storage.s3.utils.S3Keys;
35+
36+
import com.amazonaws.mobileconnectors.s3.transferutility.TransferListener;
37+
import com.amazonaws.mobileconnectors.s3.transferutility.TransferObserver;
38+
import com.amazonaws.mobileconnectors.s3.transferutility.TransferState;
39+
import com.amazonaws.services.s3.model.ObjectMetadata;
40+
41+
import java.io.IOException;
42+
import java.io.InputStream;
43+
import java.util.Objects;
44+
45+
/**
46+
* An operation to upload an InputStream from AWS S3.
47+
*/
48+
public final class AWSS3StorageUploadInputStreamOperation
49+
extends StorageUploadInputStreamOperation<AWSS3StorageUploadRequest<InputStream>> {
50+
private final StorageService storageService;
51+
private final CognitoAuthProvider cognitoAuthProvider;
52+
private final Consumer<StorageTransferProgress> onProgress;
53+
private final Consumer<StorageUploadInputStreamResult> onSuccess;
54+
private final Consumer<StorageException> onError;
55+
private TransferObserver transferObserver;
56+
57+
/**
58+
* Constructs a new AWSS3StorageUploadInputStreamOperation.
59+
* @param storageService S3 client wrapper
60+
* @param cognitoAuthProvider Interface to retrieve AWS specific auth information
61+
* @param request upload request parameters
62+
* @param onProgress Notified upon advancements in upload progress
63+
* @param onSuccess Will be notified when results of upload are available
64+
* @param onError Notified when upload fails with an error
65+
*/
66+
public AWSS3StorageUploadInputStreamOperation(
67+
@NonNull StorageService storageService,
68+
@NonNull CognitoAuthProvider cognitoAuthProvider,
69+
@NonNull AWSS3StorageUploadRequest<InputStream> request,
70+
@NonNull Consumer<StorageTransferProgress> onProgress,
71+
@NonNull Consumer<StorageUploadInputStreamResult> onSuccess,
72+
@NonNull Consumer<StorageException> onError
73+
) {
74+
super(Objects.requireNonNull(request));
75+
this.storageService = Objects.requireNonNull(storageService);
76+
this.cognitoAuthProvider = cognitoAuthProvider;
77+
this.onProgress = Objects.requireNonNull(onProgress);
78+
this.onSuccess = Objects.requireNonNull(onSuccess);
79+
this.onError = Objects.requireNonNull(onError);
80+
this.transferObserver = null;
81+
}
82+
83+
@SuppressLint("SyntheticAccessor")
84+
@Override
85+
public void start() {
86+
// Only start if it hasn't already been started
87+
if (transferObserver != null) {
88+
return;
89+
}
90+
91+
String currentIdentityId;
92+
93+
try {
94+
currentIdentityId = cognitoAuthProvider.getIdentityId();
95+
} catch (StorageException exception) {
96+
onError.accept(exception);
97+
return;
98+
}
99+
100+
String serviceKey = S3Keys.createServiceKey(
101+
getRequest().getAccessLevel(),
102+
getRequest().getTargetIdentityId() != null
103+
? getRequest().getTargetIdentityId()
104+
: currentIdentityId,
105+
getRequest().getKey()
106+
);
107+
108+
// Grab the inputStream to upload...
109+
InputStream inputStream = getRequest().getLocal();
110+
111+
// Set up the metadata
112+
ObjectMetadata objectMetadata = new ObjectMetadata();
113+
objectMetadata.setUserMetadata(getRequest().getMetadata());
114+
objectMetadata.setContentType(getRequest().getContentType());
115+
116+
ServerSideEncryption storageServerSideEncryption = getRequest().getServerSideEncryption();
117+
if (!ServerSideEncryption.NONE.equals(storageServerSideEncryption)) {
118+
objectMetadata.setSSEAlgorithm(storageServerSideEncryption.getName());
119+
}
120+
121+
// Upload!
122+
try {
123+
transferObserver = storageService.uploadInputStream(serviceKey, inputStream, objectMetadata);
124+
transferObserver.setTransferListener(new UploadTransferListener());
125+
} catch (IOException ioException) {
126+
onError.accept(new StorageException(
127+
"Issue uploading inputStream.",
128+
ioException,
129+
"See included exception for more details and suggestions to fix."
130+
));
131+
}
132+
}
133+
134+
@Override
135+
public void cancel() {
136+
if (transferObserver != null) {
137+
try {
138+
storageService.cancelTransfer(transferObserver);
139+
} catch (Exception exception) {
140+
onError.accept(new StorageException(
141+
"Something went wrong while attempting to cancel your AWS S3 Storage " +
142+
"upload input stream operation",
143+
exception,
144+
"See attached exception for more information and suggestions"
145+
));
146+
}
147+
}
148+
}
149+
150+
@Override
151+
public void pause() {
152+
if (transferObserver != null) {
153+
try {
154+
storageService.pauseTransfer(transferObserver);
155+
} catch (Exception exception) {
156+
onError.accept(new StorageException(
157+
"Something went wrong while attempting to pause your AWS S3 Storage " +
158+
"upload input stream operation",
159+
exception,
160+
"See attached exception for more information and suggestions"
161+
));
162+
}
163+
}
164+
}
165+
166+
@Override
167+
public void resume() {
168+
if (transferObserver != null) {
169+
try {
170+
storageService.resumeTransfer(transferObserver);
171+
} catch (Exception exception) {
172+
onError.accept(new StorageException(
173+
"Something went wrong while attempting to resume your AWS S3 Storage " +
174+
"upload input stream operation",
175+
exception,
176+
"See attached exception for more information and suggestions"
177+
));
178+
}
179+
}
180+
}
181+
182+
@SuppressLint("SyntheticAccessor")
183+
private final class UploadTransferListener implements TransferListener {
184+
@Override
185+
public void onStateChanged(int transferId, TransferState state) {
186+
Amplify.Hub.publish(HubChannel.STORAGE,
187+
HubEvent.create(StorageChannelEventName.UPLOAD_STATE, state.name()));
188+
switch (state) {
189+
case COMPLETED:
190+
onSuccess.accept(StorageUploadInputStreamResult.fromKey(getRequest().getKey()));
191+
return;
192+
case FAILED:
193+
onError.accept(new StorageException(
194+
"Storage upload operation was interrupted.",
195+
"Please verify that you have a stable internet connection."
196+
));
197+
return;
198+
default:
199+
// no-op;
200+
}
201+
}
202+
203+
@Override
204+
public void onProgressChanged(int transferId, long bytesCurrent, long bytesTotal) {
205+
onProgress.accept(new StorageTransferProgress(bytesCurrent, bytesTotal));
206+
}
207+
208+
@Override
209+
public void onError(int transferId, Exception exception) {
210+
Amplify.Hub.publish(HubChannel.STORAGE,
211+
HubEvent.create(StorageChannelEventName.UPLOAD_ERROR, exception));
212+
onError.accept(new StorageException(
213+
"Something went wrong with your AWS S3 Storage upload input stream operation",
214+
exception,
215+
"See attached exception for more information and suggestions"
216+
));
217+
}
218+
}
219+
}

0 commit comments

Comments
 (0)