Android with Retrofit2 OkHttp3 - Multipart POST Error

匿名 (未验证) 提交于 2019-12-03 02:20:02

问题:

I am using Retrofit2 with OkHttp on Android for HTTP request. Here I am doing a POST request with document upload. I ran into error below:

D/OkHttp: <-- 500 Server Error http://api.drivewealth.io/v1/documents (4289ms) D/OkHttp: Date: Tue, 11 Apr 2017 03:29:48 GMT D/OkHttp: Cache-Control: must-revalidate,no-cache,no-store D/OkHttp: Content-Type: text/html; charset=ISO-8859-1 D/OkHttp: Server: Jetty(9.2.17.v20160517) D/OkHttp: Content-Length: 9323 D/OkHttp: Connection: keep-alive D/OkHttp: <html> D/OkHttp: <head> D/OkHttp: <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> D/OkHttp: <title>Error 500 Server Error</title> D/OkHttp: </head> D/OkHttp: <body><h2>HTTP ERROR 500</h2> D/OkHttp: <p>Problem accessing /v1/documents. Reason: D/OkHttp: <pre>    Server Error</pre></p><h3>Caused by:</h3><pre>org.apache.cxf.interceptor.Fault: Couldn&apos;t determine the boundary from the message! D/OkHttp:     at org.apache.cxf.interceptor.AttachmentInInterceptor.handleMessage(AttachmentInInterceptor.java:60) D/OkHttp:     at org.apache.cxf.jaxrs.ext.MessageContextImpl.createAttachments(MessageContextImpl.java:279) D/OkHttp:     at org.apache.cxf.jaxrs.ext.MessageContextImpl.get(MessageContextImpl.java:77) D/OkHttp:     at org.apache.cxf.jaxrs.impl.tl.ThreadLocalMessageContext.get(ThreadLocalMessageContext.java:42) D/OkHttp:     at org.apache.cxf.jaxrs.utils.multipart.AttachmentUtils.getMultipartBody(AttachmentUtils.java:114) D/OkHttp:     at org.apache.cxf.jaxrs.utils.multipart.AttachmentUtils.getAttachments(AttachmentUtils.java:119) 

The full debug log is uploaded here

The API server requires this format for the HTTP POST multipart request:

My code snippet as below:

1) create Retrofit Handler class:

Interceptor headerInterceptor = new Interceptor() {     @Override     public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException {         Request original = chain.request();          String sessionKey = JStockApplication.instance().getTradingOptions().getSessionKey();          okhttp3.Request request = original.newBuilder()                 //.header("Accept", "application/json")                 .header("Content-Type", "multipart/form-data")                 .header("x-mysolomeo-session-key", sessionKey)                 .method(original.method(), original.body())                 .build();          return chain.proceed(request);     } };  OkHttpClient.Builder httpClient = new OkHttpClient.Builder();  // add static common headers httpClient.addInterceptor(headerInterceptor);  // add Logging for development, Log Level: NONE, BASIC, HEADERS, BODY HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor()         .setLevel(HttpLoggingInterceptor.Level.BODY); httpClient.addInterceptor(logInterceptor);  Retrofit.Builder builder = new Retrofit.Builder()         .baseUrl("http://api.drivewealth.io/v1/")         .addConverterFactory(GsonConverterFactory.create())         .client(httpClient.build());  Retrofit retrofit = builder.build();  DriveWealthApi api = retrofit.create(DriveWealthApi.class); 

2) Interface class for Retrofit method:

import okhttp3.MultipartBody; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.Body; import retrofit2.http.Multipart; import retrofit2.http.POST; import retrofit2.http.Part;  public interface DriveWealthApi {     @Multipart     @POST("documents")     Call<ResponseBody> addDocument(             @Part("token") RequestBody token,             @Part("documentType") RequestBody documentType,             @Part MultipartBody.Part file); } 

3) In my Fragment Class, the POST request is invoked in onCreate():

public class AddDocumentTaskFragment extends Fragment implements Callback<ResponseBody> {     @Override     public void onCreate(Bundle savedInstanceState) {         ........         ........          Bundle bundle = this.getArguments();         String userID = bundle.getString(INTENT_EXTRA_USER_ID);         String docType = bundle.getString(INTENT_EXTRA_DOCUMENT_TYPE);         String fileUri = bundle.getString(INTENT_EXTRA_FILE_URI);         Uri uri = Uri.parse(fileUri);          String filePath = MyUtils.getPath(this.getActivity(), uri);          if (filePath == null || filePath.isEmpty()) {             return;         }          final File myFile = new File(filePath);         MediaType mediaType = MediaType.parse(getActivity().getContentResolver().getType(uri));          if (myFile == null) {             return;         }          // create RequestBody instance from file         RequestBody requestFile = RequestBody.create(mediaType, myFile);          // MultipartBody.Part is used to send also the actual file name         MultipartBody.Part fileBody = MultipartBody.Part.createFormData("documentImage", myFile.getName(), requestFile);          // add another part within the multipart request         RequestBody tokenBody = RequestBody.create(okhttp3.MultipartBody.FORM, userID);          RequestBody docTypeBody = RequestBody.create(okhttp3.MultipartBody.FORM, docType);          // params: token, documentType, file         this.call = driveWealthApi.addDocument(tokenBody, docTypeBody, fileBody);         this.call.enqueue(this);     }      @Override     public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {         .....     }      @Override     public void onFailure(Call<ResponseBody> call, Throwable t) {         ......     } 

Any idea what went wrong here? Thanks!

回答1:

After referring to Retrofit 2 can't upload a file with two additional separate string parameters , I implemented according to @TommySM suggestion. I got my problem solved with this:

// create RequestBody instance from file RequestBody requestFile = RequestBody.create(             mediaType,             myFile);  // MultipartBody.Part is used to send also the actual file name MultipartBody.Part file = MultipartBody.Part.createFormData(             "documentImage",             myFile.getName(),             requestFile);  // add another part within the multipart request RequestBody token = RequestBody.create(             MediaType.parse("text/plain"),   // Fixed here             //okhttp3.MultipartBody.FORM,    => PROBLEMATIC             userID);  RequestBody docType = RequestBody.create(             MediaType.parse("text/plain"),   // Fixed here             //okhttp3.MultipartBody.FORM,    => PROBLEMATIC             docTypeStr);  // token, documentType, file this.call = driveWealthApi.addDocument(token, docType, file); 

Those String parameter should be specified as Content-Type: text/plain rather than Content-Type: multipart/form-data.

See screenshots for details:

1) Problematic POST

2) Correct POST



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!