将多部分/表单数据上传到S3存储桶会抛出SSLException

时间:2019-06-24 15:05:58

标签: android amazon-s3 retrofit2 rx-java2 okhttp3

不幸的是,当我想将相当大的(〜100Mb)二进制文件上传到AWS S3存储桶时,我陷入了困境,非常感谢您的帮助:-)

相关的已使用库:

  • Retrofit2(2.5.0)
  • okhttp3(3.14.2)
  • rxjava2:rxandroid(2.1.1)
  • rxjava2:rxjava(2.2.9)

这是我的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 

0 个答案:

没有答案