Android在单个请求中上载多个文件

时间:2016-12-17 01:27:22

标签: android multipartform-data retrofit2

我在 multipart / form-data 请求中将内容发布到服务器。 我发布的数据包含多个参数,包括文件数组参数(files [])。

使用postman我将参数和文件设置为:

first-parameter=text content
second-parameter=text content
files[0]={first selected file}
files[1]={second selected file}

在邮递员上提交此请求总是成功的,文件上传成功。

当我在邮递员上生成代码片段时,结果如下:

POST /api/*******/new HTTP/1.1
Host: ***.***.***.*** ip-address
Authorization: {authorization header}
Cache-Control: no-cache
Postman-Token: d15f13f1-4a65-81d1-bf91-f5accd1b1dd0
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="first-parameter"

first-parameter-value
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="second-parameter"

second-parameter-value
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="files[0]"; filename=""
Content-Type: 

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="files[1]"; filename=""
Content-Type: 

------WebKitFormBoundary7MA4YWxkTrZu0gW--

在Android上,我正在使用HttpLoggingInterceptor库进行Retrofit,并在发布请求时使用@PartMap注释:

// API定义:

interface ApiDefinitions {

@Multipart
@POST("*******/new")
Call<ApiResponse> submitNew(@Header("Authorization") String authHeader,
                                      @PartMap Map<String, RequestBody> params);

}

//准备并发送请求代码:

public void postContent() {
    HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
    interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient client = new OkHttpClient
            .Builder()
            .addInterceptor(interceptor)
            .build();

    Retrofit retrofit = new Retrofit
            .Builder()
            .baseUrl(API_URL)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create(new Gson()))
            .build();

    ApiDefinitions api = retrofit.create(ApiDefinitions.class);

    MediaType mediaType = MediaType.parse("multipart/form-data");

    Map<String, RequestBody> params = new HashMap<>();
    params.put("first-parameter", MultipartBody.create(mediaType, "first-parameter-value"));
    params.put("second-parameter", MultipartBody.create(mediaType, "second-parameter-value"));
    params.put("files[0]", MultipartBody.create(mediaType, new File("First file path")));
    params.put("files[1]", MultipartBody.create(mediaType, new File("Second file path")));

    Call<ApiResponse> call = api.submitNew("Auth Token", params);
    enqueue.enqueue(new Callback<ApiResponse>() {

        @Override
        public void onResponse(Call<ApiResponse> call, Response<ApiResponse> response) {
        }

        @Override
        public void onFailure(Call<ApiResponse> call, Throwable t) {
        }
    });
}

提交此请求无误,http响应为200,文件未上传!!!

由于我正在使用HttpLoggingInterceptor,我尝试将改装日志与post man代码片段进行比较,这是改造日志:

D/OkHttp: --> POST http://{api_address}/api/*****/new http/1.1
D/OkHttp: Content-Type: multipart/form-data; boundary=64360751-a7f4-44c4-a008-f5de764c7298
D/OkHttp: Content-Length: 119325
D/OkHttp: Authorization: {Authorization-Token}
D/OkHttp: --64360751-a7f4-44c4-a008-f5de764c7298
D/OkHttp: Content-Disposition: form-data; name="first-parameter"
D/OkHttp: Content-Transfer-Encoding: binary
D/OkHttp: Content-Type: multipart/form-data; charset=utf-8
D/OkHttp: Content-Length: 10
D/OkHttp: first-parameter-value
D/OkHttp: --64360751-a7f4-44c4-a008-f5de764c7298
D/OkHttp: Content-Disposition: form-data; name="second-parameter"
D/OkHttp: Content-Transfer-Encoding: binary
D/OkHttp: Content-Type: multipart/form-data; charset=utf-8
D/OkHttp: Content-Length: 10
D/OkHttp: second-parameter-value
D/OkHttp: --64360751-a7f4-44c4-a008-f5de764c7298
D/OkHttp: Content-Disposition: form-data; name="files[0]"
D/OkHttp: Content-Transfer-Encoding: binary
D/OkHttp: Content-Type: multipart/form-data;
D/OkHttp: Content-Length: 44205
D/OkHttp: �PNG
D/OkHttp: 
D/OkHttp: ������IHDR��������������������r�B�������sBIT��O����� ��IDATx�4�˳mYv��}c�9���so�|W���R��2z�T%�8B�X� 
D/OkHttp: &�D$��B��r�D��w�!@��������{��H���[�!,�@ �4h�P����>�A��&� ����B�[�,�fD�Mi�d�5)���5�{��-�MQt��ٗ&
D/OkHttp: --64360751-a7f4-44c4-a008-f5de764c7298
D/OkHttp: Content-Disposition: form-data; name="files[1]"
D/OkHttp: Content-Transfer-Encoding: binary
D/OkHttp: Content-Type: multipart/form-data;
D/OkHttp: Content-Length: 44205
D/OkHttp: �PNG
D/OkHttp: 
D/OkHttp: ������IHDR��������������������r�B�������sBIT��O����� ��IDATx�4�˳mYv��}c�9���so�|W���R��2z�T%�8B�X� 
D/OkHttp: &�D$��B��r�D��w�!@��������{��H���[�!,�@ �4h�P����>�A��&� ����B�[�,�fD�Mi�d�5)���5�{��-�MQt��ٗ&
D/OkHttp: --64360751-a7f4-44c4-a008-f5de764c7298--
D/OkHttp: --> END POST (119325-byte body)
D/OkHttp: <-- 200 OK http://{api_address}/api/*****/new (3409ms)

我试图通过比较邮递员与改装后的日志来查找我的请求中的错误,但我找不到它! 随机边界字符串是不同的,这是正常的,邮递员使用webkit,而改造不是!我认为这根本不是问题。

我尝试使用@Part List<MultipartBody.Part代替@PartMap Map<String, RequestBody>来处理建议here的文件,但它也不起作用。

我应该怎样处理要上传的文件?

2 个答案:

答案 0 :(得分:2)

首先,我在@PartMap Map<String, RequestBody>界面中使用ApiDefinitions做错了。

如果您正在使用多个参数和多个文件执行发布请求,请始终确保在定义api方法时使用RequestBody作为非文件参数,并使用MultipartBody.Part作为文件参数

在我的情况下,我需要发送一组文件,因此对我有用的参数是MultipartBody.Part[]

这是新的api定义:

@Multipart
@POST("*******/new")
Call<ApiResponse> submitNew(@Header("Authorization") String authHeader,
                                          @Part("first-parameter") RequestBody firstParameter,
                                          @Part("first-parameter") RequestBody secondParameter,
                                          @Part MultipartBody.Part[] files);

我犯的第二个错误并没有注意到这一点:

  

PostMan Log:Content-Disposition:form-data;名称= “文件[0]”;文件名= “”

     

改造日志:内容处理:表格数据;名称= “文件[0]”

文件名未包含在多部分请求中,显然是known issue when uploading files to a php service

我没有制作api,我没有任何php背景所以请不要评判我。 据我所知,我成功地将文件发送到服务api,但是api不知道如何保存这些文件!

所以在发送请求时:

List<Uri> files; //These are the uris for the files to be uploaded
MediaType mediaType = MediaType.parse("");//Based on the Postman logs,it's not specifying Content-Type, this is why I've made this empty content/mediaType
MultipartBody.Part[] fileParts = new MultipartBody.Part[files.size()];
for (int i = 0; i < files.size(); i++) {
    File file = new File(files.get(i).getPath());
    RequestBody fileBody = RequestBody.create(mediaType, file);
    //Setting the file name as an empty string here causes the same issue, which is sending the request successfully without saving the files in the backend, so don't neglect the file name parameter.
    fileParts[i] = MultipartBody.Part.createFormData(String.format(Locale.ENGLISH, "files[%d]", i), file.getName(), fileBody);
}

Call<ApiResponse> call = api.submitNew("Auth Token", MultipartBody.create(mediaType, "first_parameter_values"), MultipartBody.create(mediaType, "second_parameter_values"), fileParts);

call.enqueue(new Callback<ApiResponse>() {

    @Override
    public void onResponse(Call<ApiResponse> call, Response<ApiResponse> response) {
    }

    @Override
    public void onFailure(Call<ApiResponse> call, Throwable t) {
    }
});

答案 1 :(得分:0)

我认为您应该更改文件的MediaType。并且您还检查了https://futurestud.io/tutorials/retrofit-2-how-to-upload-files-to-server的链接和@Part MultipartBody.Part的发送文件

MediaType mediaTypeForText = MediaType.parse(&#34; text / plain&#34;); MediaType mediaTypeForImage = MediaType.parse(&#34; image / *&#34;);

Map params = new HashMap&lt;&gt;(); params.put(&#34; first-parameter&#34;,MultipartBody.create(mediaTypeForText,&#34; first-parameter-value&#34;)); params.put(&#34; second-parameter&#34;,MultipartBody.create(mediaTypeForText,&#34; second-parameter-value&#34;)); params.put(&#34; files [0]&#34;,MultipartBody.create(mediaTypeForImage,new File(fileUri.getPath())); params.put(&#34; files [1]&#34; ,MultipartBody.create(mediaTypeForImage,new File(fileUri.getPath()));