本文介绍了带有Retrofit2 OkHttp3的Android-多部分POST错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将Retrofit2与OkHttp一起在Android上用于HTTP请求.在这里,我正在执行带有文件上传的POST请求.我在下面遇到错误:

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

API服务器要求HTTP POST多部分请求使用以下格式:

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

我的代码段如下:

1)创建Retrofit Handler类:

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)Retrofit方法的接口类:

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)在我的Fragment类中,POST请求在onCreate()中被调用:

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!

推荐答案

参考,我根据@TommySM建议实现了该功能.我通过以下方法解决了我的问题:

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);

应将这些String参数指定为Content-Type: text/plain而不是Content-Type: multipart/form-data.

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

有关详细信息,请参见屏幕截图:

See screenshots for details:

1)有问题的POST

1) Problematic POST

2)正确的POST

2) Correct POST

这篇关于带有Retrofit2 OkHttp3的Android-多部分POST错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-30 07:35