Retrofit.java.net.ProtocolException: 预期的 * 字节但已收到 *



我正在尝试通过 Retrofit2 执行多部分 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();
        }
    });
}

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

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

我解决了这个问题,创建了文件的副本并发送它(副本(而不是原始文件。

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

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

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

在科特林。

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
}

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

这是我用于所有请求的内容,完全没有问题。如果它对您不起作用,请告诉我。我假设您文件的 POST 名称是"文件"。

在协议中:

@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:由于我在使用此方法时遇到的几个问题,我不得不添加以下代码以确保它永远不会发送空或空映射。

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

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

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

简短回答:

删除了代码中的以下行,一切都运行良好:

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

长答案:

对于多部分上传,我们必须写入一些额外的数据,例如 boundarytwoHyphens (--)newLine (rn) ...因此,数据的长度将比数据的长度(文件/位图/缓冲(长。

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

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

相关内容

最新更新