I'm posting content to the server in a multipart/form-data request. and the data I'm posting contains multiple parameters including a file array parameter (files[]).
Using postman I'm setting the parameters and the files as:
first-parameter=text content
second-parameter=text content
files[0]={first selected file}
files[1]={second selected file}
Submitting this request on postman is always successful, and the files are uploaded successfully.
When I generate code snippets on postman, this is the result:
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--
On Android, I'm using Retrofit with the HttpLoggingInterceptor libraries, and using the @PartMap annotation when posting the request:
//API Definition:
interface ApiDefinitions {
@Multipart
@POST("*******/new")
Call<ApiResponse> submitNew(@Header("Authorization") String authHeader,
@PartMap Map<String, RequestBody> params);
}
//Preparing and sending the request code:
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) {
}
});
}
Submitting this request works with no errors, http response is 200, But the files are not uploaded !!!
Since I'm using the HttpLoggingInterceptor, I've tried comparing the retrofit logs with post man code snippets, this is the retrofit log:
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)
I've tried to find what's wrong in my request by comparing both logs from postman vs retrofit, but I can't find it ! The random boundary strings are different which is normal, postman uses webkit, while retrofit does not ! I don't think this is an issue at all.
I've tried to use @Part List<MultipartBody.Part
instead of @PartMap Map<String, RequestBody>
for the files as suggested here, but it didn't work also.
What should I do for the files to be uploaded ?
I think you should change the MediaType for file. and you also check the link for that https://futurestud.io/tutorials/retrofit-2-how-to-upload-files-to-server and for send file with @Part MultipartBody.Part
MediaType mediaTypeForText = MediaType.parse("text/plain"); MediaType mediaTypeForImage = MediaType.parse("image/*");
Map params = new HashMap<>(); params.put("first-parameter", MultipartBody.create(mediaTypeForText, "first-parameter-value")); params.put("second-parameter", MultipartBody.create(mediaTypeForText, "second-parameter-value")); params.put("files[0]", MultipartBody.create(mediaTypeForImage, new File(fileUri.getPath())); params.put("files[1]", MultipartBody.create(mediaTypeForImage, new File(fileUri.getPath()));
First of all, I've been doing one thing wrong which is using a
@PartMap Map<String, RequestBody>
in theApiDefinitions
interface.In case you're doing a post request with multiple parameters and multiple files, always make sure when defining the api method to use
RequestBody
for non-file parameters, and useMultipartBody.Part
for file parameters.In my case I needed to send an array of files, so the parameter that has worked for me is
MultipartBody.Part[]
.This is the new api definition :
The second mistake I made was not noticing this:
The filename was not included in the multipart request, which apparently is a known issue when uploading files to a php service !
I didn't make the api, and I don't have any php background so please don't judge me. As far as I know, I was successfully sending files to the service api, but the api didn't know how to save these files !
So when sending the request: