其他分享
首页 > 其他分享> > Android在一个请求中上传多个文件

Android在一个请求中上传多个文件

作者:互联网

我正在通过多部分/表单数据请求将内容发布到服务器.
并且我发布的数据包含多个参数,包括文件数组参数(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上,我将Retrofit与HttpLoggingInterceptor库一起使用,并在发布请求时使用@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,因此我尝试将改造日志与邮递员代码段进行比较,这是改造日志:

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,但它也无法正常工作.

上传文件该怎么办?

解决方法:

首先,我做错了一件事情,那就是使用@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; name=”files[0]”; filename=””

Retrofit Log: Content-Disposition: form-data; name=”files[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) {
    }
});

标签:android,retrofit2,multipartform-data
来源: https://codeday.me/bug/20191013/1907036.html