改造。 java.net.ProtocolException:expected * bytes但收到*

时间:2017-06-30 11:54:24

标签: android rest retrofit2

我正在尝试通过Retrofit2执行Multipart POST请求,我将API上传到自定义文件。

随着此异常随机失败:

W/System.err: java.net.ProtocolException: expected 154 bytes but received 634

有人可以对它有所了解吗?

这是我在界面中的代码:

@Multipart
@POST("recordings/{id}/{rec_id}/")
Call<ResponseBody> uploadRecording(@Path("id") String id, @Path("rec_id") String rec_id, @Part MultipartBody.Part bleFile);

在构造函数中:

public ApiConnectionManager(Context con){
    Gson gson = new GsonBuilder()
            .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
            .create();

    OkHttpClient.Builder client = new OkHttpClient.Builder();
    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
    client.addInterceptor(loggingInterceptor);

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(con.getResources().getString(R.string.api_url)) // API url is hidden
            .addConverterFactory(GsonConverterFactory.create(gson))
            .client(client.build())
            .build();

    this.companyAPI = retrofit.create(CompanyAPI.class);
}

并在上传方法中:

private void uploadFile(String id, final File bleFile) {
    MediaType MEDIA_TYPE = MediaType.parse("multipart/mixed");
    RequestBody requestBody = RequestBody.create(MEDIA_TYPE,bleFile);
    MultipartBody.Part partFile = MultipartBody.Part.createFormData("file", bleFile.getName(), requestBody);
    String recordingId = bleFile.getName().replace(".BLE","");
    Call<ResponseBody> call = companyAPI.uploadRecording(id, recordingId, partFile);
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            Log.d(TAG+"-Upload "+bleFile.getName(),response.message());
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.d(TAG,"FAILED");
            t.printStackTrace();
        }
    });
}

6 个答案:

答案 0 :(得分:3)

经过一段时间的研究后,我意识到文件的内容总是在变化(因为它是传感器的输出)。

这意味着为HEAD检查的文件和BODY的文件可能不包含相同的数据(因此长度不同),这导致了不匹配。

我解决了文件的creating a copy并发送它(副本)而不是原始文件。

答案 1 :(得分:2)

我在尝试上传录音时遇到了这个问题。我通过在调用Web服务上传文件之前停止录制过程来解决它。

objMediaRecorder.stop();
objMediaRecorder.release();
objMediaRecorder = null;

答案 2 :(得分:1)

这是我用于所有请求的内容,并且完全没有任何问题。如果它不适合你,请告诉我。我假设您的文件的POST名称是“file”。

在协议中:

@Multipart
@POST
Call<ResponseBody> request(
        @Url String url, // Request URL
        @PartMap Map<String, String> vars, // POST Strings
        @PartMap Map<String, RequestBody> files // POST Files
);

构建一个电话:

Map<String, String> vars = new HashMap<>();
Map<String, RequestBody> files = new HashMap<>();

/** Put a string **/

vars.put("name", "value");

/** Put a file **/

String key = String.format(Locale.US, "file\"; filename=\"%s", file.getName());
RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
files.put(key, requestBody);

/** Construct the call **/

Call<ResponseBody> call = mProtocol.request(url, vars, files);

call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            Log.d("Debug", response.body().string());
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            if (call.isCanceled()) Log.d("Debug", "Call Canceled");
            else Log.d("Debug", "Call Failed: " + t.toString());
        }
});

PS:您可以使用这段代码上传多个文件,因为它接受的是文件地图而不是单个文件。

PS#2:由于我使用此方法遇到了一些问题,我不得不添加以下代码以确保它永远不会发送null或空映射。

if (vars == null) {
    vars = new HashMap<>();
    vars.put("01110011", "01101101"); // put whatever you want
}
if (files == null) files = new HashMap<>();

答案 3 :(得分:1)

我遇到了相同的问题,并通过在上传之前创建一个临时文件来解决了该问题。

在科特林。

fun createTemoraryFile(context: Context, uri: Uri): File {
    val inputStream = context.contentResolver.openInputStream(uri)
    val f = createTempFile(
        directory = context.cacheDir
        )
    inputStream.copyTo(f.outputStream())
    return f
}

上传完成后,我删除了临时文件。

答案 4 :(得分:0)

这意味着您尝试发送的文件仍在创建中,并且其大小正在发生变化。您应该先完成文件,然后再发送。

答案 5 :(得分:0)

就我而言,我使用 HttpUrlConnection 上传文件/位图/缓冲区作为多部分协议上传...

简答:

删除了代码中的以下行,一切正常:

// remove this line 
connection.setFixedLengthStreamingMode(dataToUpload.length);

长答案:

对于分段上传我们必须写一些额外的数据,例如boundarytwoHyphens (--)newLine (\r\n)...因此,数据的长度会比上传的长度长数据(文件/位图/缓冲)。

通过删除连接请求中的setFixedLengthStreamingMode,我们可以解决这个问题。

但是,如果需要发送这个文件或数据长度,我们必须计算总长度