【问题标题】:Retrofit. java.net.ProtocolException: expected * bytes but received *改造。 java.net.ProtocolException: 预期 * 字节但收到 *
【发布时间】:2017-12-04 09:06:51
【问题描述】:

我正在尝试通过 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();
        }
    });
}

【问题讨论】:

    标签: android rest retrofit2


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

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

        在 Kotlin 中。

        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】:

          这是我用于所有请求的,完全没有问题。让我知道它是否不适合您。我假设您的文件的 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:由于我在使用此方法时遇到了几个问题,我不得不添加以下代码以确保它永远不会发送 null 或空地图。

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

          【讨论】:

          • 使用注释“@PartMap”而不是“@Part”解决了这个问题。
          • 能否请您将 call.enqueue() 添加到此答案中,以便有相同问题的人可以有一段工作代码?
          • @kike 我已经改变了我的答案,还添加了如何在文件之外发送字符串。
          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

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

            简短回答:

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

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

            长答案:

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

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2018-09-06
              • 2018-01-17
              • 2015-10-18
              • 1970-01-01
              • 2015-07-24
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多