不幸的是,当我想将相当大的(〜100Mb)二进制文件上传到AWS S3存储桶时,我陷入了困境,非常感谢您的帮助:-)
相关的已使用库:
这是我的UploadIntentService.java
的一些摘要:
private void scheduleUpload(final File file, final ContainerFilesObject containerFilesObject) {
final String uploadUrl = containerFilesObject.getUploadUrl();
Log.d(TAG, String.format("scheduleUpload() for file: %s | URL: %s", file, uploadUrl));
final HashMap<String, RequestBody> partMap = createPartMap(containerFilesObject.getUploadParams());
uploadFileDisposable = uploadFile(uploadUrl, partMap, file)
.subscribe(
new Consumer<Long>() {
@Override
public void accept(Long bytesWritten) throws Exception {
Log.v(TAG, "uploadFile() --> accept bytesWritten = " + bytesWritten);
notifyFileProgress(bytesWritten);
}
},
new Consumer<Throwable>() {
@Override
public void accept(Throwable error) throws Exception {
Log.e(TAG, "uploadFile() --> ERROR --> onUploadFailed()", error);
UploadIntentService.this.onUploadFailed();
}
},
new Action() {
@Override
public void run() throws Exception {
Log.i(TAG, "uploadFile() --> onComplete() --> handleFileUploaded() && disposeUpload()");
UploadIntentService.this.handleFileUploaded(file);
disposeUpload();
}
}
);
}
private HashMap<String, RequestBody> createPartMap(final UploadParams uploadParams) {
final RequestBody key = RequestBody.create(MediaType.parse(MIME_TEXT), uploadParams.getKey());
final RequestBody bucket = RequestBody.create(MediaType.parse(MIME_TEXT), uploadParams.getBucket());
final RequestBody policy = RequestBody.create(MediaType.parse(MIME_TEXT), uploadParams.getPolicy());
final RequestBody xAmzAlgorithm = RequestBody.create(MediaType.parse(MIME_TEXT), uploadParams.getXAmzAlgorithm());
final RequestBody xAmzCredential = RequestBody.create(MediaType.parse(MIME_TEXT), uploadParams.getXAmzCredential());
final RequestBody xAmzDate = RequestBody.create(MediaType.parse(MIME_TEXT), uploadParams.getXAmzDate());
final RequestBody xAmzSignature = RequestBody.create(MediaType.parse(MIME_TEXT), uploadParams.getXAmzSignature());
final HashMap<String, RequestBody> partMap = new HashMap<>();
partMap.put(API3_UPLOADPARAM_KEY, key);
partMap.put(API3_UPLOADPARAM_BUCKET, bucket);
partMap.put(API3_UPLOADPARAM_POLICY, policy);
partMap.put(API3_UPLOADPARAM_X_AMZ_ALGORITHM, xAmzAlgorithm);
partMap.put(API3_UPLOADPARAM_X_AMZ_CREDENTIAL, xAmzCredential);
partMap.put(API3_UPLOADPARAM_X_AMZ_DATE, xAmzDate);
partMap.put(API3_UPLOADPARAM_X_AMZ_SIGNATURE, xAmzSignature);
return partMap;
}
private Flowable<Long> uploadFile(final String uploadUrl, final HashMap<String, RequestBody> partMap, final File file) {
return Flowable.create(new FlowableOnSubscribe<Long>() {
@Override
public void subscribe(FlowableEmitter<Long> emitter) throws Exception {
try {
final MultipartBody.Part filePart = UploadIntentService.this.createMultipartBodyPart(file, emitter);
ResponseBody response = repository.uploadFileWithPartMap(uploadUrl, partMap, filePart).blockingGet();
Log.i(TAG, "uploadFile() --> emitter.onComplete() for response: " + response);
emitter.onComplete();
} catch (Exception e) {
Log.e(TAG, "uploadFile() --> emitter.tryOnError(e)", e);
emitter.tryOnError(e);
}
}
}, BackpressureStrategy.LATEST);
}
private MultipartBody.Part createMultipartBodyPart(final File file, final FlowableEmitter<Long> emitter) {
final RequestBody requestBody = createCountingRequestBody(file, emitter);
return MultipartBody.Part.createFormData("upload", file.getName(), requestBody);
}
private RequestBody createCountingRequestBody(final File file, final FlowableEmitter<Long> emitter) {
final RequestBody requestBody = RequestBody.create(MediaType.parse(MIME_MULTIPART), file);
return new CountingRequestBody(requestBody, (bytesWritten, contentLength) -> {
emitter.onNext(bytesWritten);
});
}
所以我在这里的第一个问题是:我是否错过了createPartMap
方法中的任何内容?
来自RetrofitRepository.java
的摘要:
@Override
public Single<ResponseBody> uploadFileWithPartMap(final String uploadUrl, final HashMap<String, RequestBody> partMap, final MultipartBody.Part requestBody) {
return restApi.uploadFileWithPartMap(uploadUrl, partMap, requestBody)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
来自RestApi.java
的摘要:
@Multipart
@POST
Single<ResponseBody> uploadFileWithPartMap(
@Url String uploadUrl,
@PartMap Map<String, RequestBody> partMap,
@Part MultipartBody.Part file
);
这是我的logcat输出:
2019-07-03 08:59:39.590 8139-8230/com.foo.player.debug I/UploadIntentService: tryUploadFiles() --> directly starting upload for to: https://s3.eu-central-1.amazonaws.com/foo-session-data
2019-07-03 08:59:39.591 8139-8230/com.foo.player.debug D/UploadIntentService: scheduleUpload() for file: /data/user/0/com.foo.player.debug/app_transferredSessions/Zoo43/session_Tae90-az12pWjw0E.bin | URL: https://s3.eu-central-1.amazonaws.com/foo-session-data
2019-07-03 08:59:39.624 8139-8194/com.foo.player.debug D/OkHttp: --> POST https://s3.eu-central-1.amazonaws.com/foo-session-data
2019-07-03 08:59:39.625 8139-8194/com.foo.player.debug D/OkHttp: Content-Type: multipart/form-data; boundary=91b0db3a-2e56-4ea2-aa9b-de68bd7912da
2019-07-03 08:59:39.627 8139-8194/com.foo.player.debug D/OkHttp: Content-Length: 13048908
2019-07-03 08:59:39.627 8139-8194/com.foo.player.debug D/OkHttp: --> END POST
2019-07-03 08:59:40.175 8139-8194/com.foo.player.debug D/Ok2Curl: curl -X POST -H "Content-Type:multipart/form-data; boundary=91b0db3a-2e56-4ea2-aa9b-de68bd7912da" -d '--91b0db3a-2e56-4ea2-aa9b-de68bd7912da
Content-Disposition: form-data; name="Policy"
Content-Transfer-Encoding: binary
Content-Type: text/plain; charset=utf-8
Content-Length: 392
<myVeryLongPolicy>=
--91b0db3a-2e56-4ea2-aa9b-de68bd7912da
Content-Disposition: form-data; name="bucket"
Content-Transfer-Encoding: binary
Content-Type: text/plain; charset=utf-8
Content-Length: 22
foo-session-data
--91b0db3a-2e56-4ea2-aa9b-de68bd7912da
Content-Disposition: form-data; name="key"
Content-Transfer-Encoding: binary
Content-Type: text/plain; charset=utf-8
Content-Length: 39
az12pWjw0E/session_Tae90-az12pWjw0E.bin
--91b0db3a-2e56-4ea2-aa9b-de68bd7912da
Content-Disposition: form-data; name="X-Amz-Credential"
Content-Transfer-Encoding: binary
Content-Type: text/plain; charset=utf-8
Content-Length: 58
<fooCredential>/20190703/eu-central-1/s3/aws4_request
--91b0db3a-2e56-4ea2-aa9b-de68bd7912da
Content-Disposition: form-data; name="X-Amz-Signature"
Content-Transfer-Encoding: binary
Content-Type: text/plain; charset=utf-8
Content-Length: 64
<myAmzSignature>
--91b0db3a-2e56-4ea2-aa9b-de68bd7912da
Content-Disposition: form-data; name="X-Amz-Date"
Content-Transfer-Encoding: binary
Content-Type: text/plain; charset=utf-8
Content-Length: 16
20190703T065158Z
--91b0db3a-2e56-4ea2-aa9b-de68bd7912da
Content-Disposition: form-data; name="X-Amz-Algorithm"
Content-Transfer-Encoding: binary
Content-Type: text/plain; charset=utf-8
Content-Length: 16
AWS4-HMAC-SHA256
--91b0db3a-2e56-4ea2-aa9b-de68bd7912da
Content-Disposition: form-data; name="upload"; filename="session_Tae90-az12pWjw0E.bin"
Content-Type: multipart/form-data
Content-Length: 13046727
�����6���Lj���.~�0q���v��y 6��q���C�$���f�Y�]���
D�*�aʅѾ<�)��)�(�ۗ(IF���9Dr�˪.tZ�$b�Wl��Ot��1��i�wf=�q0�2r%4ޥzS�$�����J7�^<��k<,��P�XW�CFNy�����>�",�
���+�X�=~��Q��N��@A:T���$C��<aN����*�wXw,}�L�9g��Vv��&�+G��q"��+7��K�HK�g����2^�^���*E�&�j���ؕ{��[�p'�1��/��Vӂ�x��^��$�*5�.�������UD����&��]>�2��K���NO"�bۯܡ<���5�6��k����iBc�o�_���kFY`2���Xs���~D�v�e��^<5pp���=h�����v�k�;O�� �Oj�&�xF�[H��_h����d�M�L16V62�1���x1�|���'0�z��&�ݑ`��v+���n�?!�6�n�V�X� T�@*o5̕��Y"A"v��3��ɬl�2��}����#i��S�,8�U8���t��:�nj^bM�WB�����q/��;�tx��=G@�2u��'m6,JCa �uQ�du�,�3��g�X�J��y��?�}_z�
F�h���g���K�B<�@uoit�;=��(���.yϥ�Ax��~��`��|�N3�Z7O�����:�c��?�j�Ö����<
�X�d]�Qw�H>[�7���`�5�aFɲ�Tr�@�����D��3��'J�ѵ[Q�$c���)b�0v٣������O�Ma����Y٪,MP�ZIy��`���Ԧ(��<��kgg-��+䙜��HݕF��}��0���W��3���B��g�n~e����$��`ޤ�̣�'�!�q�#zl�"W�W����ӟ��%�0�u�D��MJ��Y��&����A�7+S�
2019-07-03 08:59:41.542 8139-8194/com.foo.player.debug D/OkHttp: <-- HTTP FAILED: javax.net.ssl.SSLException: Write error: ssl=0x8d6fc900: I/O error during system call, Connection reset by peer
2019-07-03 08:59:41.548 8139-8230/com.foo.player.debug E/UploadIntentService: uploadFile() --> emitter.tryOnError(e)
java.lang.RuntimeException: javax.net.ssl.SSLException: Write error: ssl=0x8d6fc900: I/O error during system call, Connection reset by peer
at io.reactivex.internal.util.ExceptionHelper.wrapOrThrow(ExceptionHelper.java:46)
at io.reactivex.internal.observers.BlockingMultiObserver.blockingGet(BlockingMultiObserver.java:93)
at io.reactivex.Single.blockingGet(Single.java:2835)
我缺少什么/做错了什么?
编辑:
此curl命令可以达到相同的目的(但我仍然需要以适当的方式从Android执行此操作)。
curl -X POST s3.eu-central-1.amazonaws.com/foo-session-data \
-F "key=xxx" \
-F "bucket=xxx" \
-F "X-Amz-Algorithm=AWS4-HMAC-SHA256" \
-F "X-Amz-Credential=<fooCredential>/20190624/eu-central-1/s3/aws4_request" \
-F "X-Amz-Date=20190624T130718Z" \
-F "Policy=xxx=" \
-F "X-Amz-Signature=xxx" \
-F "file=<local path>" \
-i