title: Android Upload Multiple Files In A Single Request
body: <p>I'm posting content to the server in a <b>multipart/form-data</b> request.
  and the data I'm posting contains multiple parameters including a file array parameter (files[]).</p>
  
  <p>Using <a href="https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en" rel="nofollow noreferrer">postman</a> I'm setting the parameters and the files as:</p>
  
  <pre><code>first-parameter=text content
  second-parameter=text content
  files[0]={first selected file}
  files[1]={second selected file}
  </code></pre>
  
  <p>Submitting this request on postman is always successful, and the files are uploaded successfully.</p>
  
  <p>When I generate code snippets on postman, this is the result:</p>
  
  <pre><code>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--
  </code></pre>
  
  <p>On Android, I'm using Retrofit with the HttpLoggingInterceptor libraries, and using the @PartMap annotation when posting the request:</p>
  
  <p>//API Definition:</p>
  
  <pre><code>interface ApiDefinitions {
  
  @Multipart
  @POST("*******/new")
  Call&lt;ApiResponse&gt; submitNew(@Header("Authorization") String authHeader,
                                        @PartMap Map&lt;String, RequestBody&gt; params);
  
  }
  </code></pre>
  
  <p>//Preparing and sending the request code:</p>
  
  <pre><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&lt;String, RequestBody&gt; params = new HashMap&lt;&gt;();
      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&lt;ApiResponse&gt; call = api.submitNew("Auth Token", params);
      enqueue.enqueue(new Callback&lt;ApiResponse&gt;() {
  
          @Override
          public void onResponse(Call&lt;ApiResponse&gt; call, Response&lt;ApiResponse&gt; response) {
          }
  
          @Override
          public void onFailure(Call&lt;ApiResponse&gt; call, Throwable t) {
          }
      });
  }
  </code></pre>
  
  <p>Submitting this request works with no errors, http response is 200, <b>But</b> the files are not uploaded !!!</p>
  
  <p>Since I'm using the HttpLoggingInterceptor, I've tried comparing the retrofit logs with post man code snippets, this is the retrofit log:</p>
  
  <pre><code>D/OkHttp: --&gt; 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: &amp;�D$��B��r�D��w�!@��������{��H���[�!,�@ �4h�P����&gt;�A��&amp;� ����B�[�,�fD�Mi�d�5)���5�{��-�MQt��ٗ&amp;
  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: &amp;�D$��B��r�D��w�!@��������{��H���[�!,�@ �4h�P����&gt;�A��&amp;� ����B�[�,�fD�Mi�d�5)���5�{��-�MQt��ٗ&amp;
  D/OkHttp: --64360751-a7f4-44c4-a008-f5de764c7298--
  D/OkHttp: --&gt; END POST (119325-byte body)
  D/OkHttp: &lt;-- 200 OK http://{api_address}/api/*****/new (3409ms)
  </code></pre>
  
  <p>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.</p>
  
  <p>I've tried to use <code>@Part List&lt;MultipartBody.Part</code> instead of <code>@PartMap Map&lt;String, RequestBody&gt;</code> for the files as suggested <a href="https://github.com/square/retrofit/issues/1776" rel="nofollow noreferrer">here</a>, but it didn't work also.</p>
  
  <p>What should I do for the files to be uploaded ?</p>