我正在尝试通过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();
}
});
}
答案 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);
长答案:
对于分段上传我们必须写一些额外的数据,例如boundary
、twoHyphens (--)
、newLine (\r\n)
...因此,数据的长度会比上传的长度长数据(文件/位图/缓冲)。
通过删除连接请求中的setFixedLengthStreamingMode
,我们可以解决这个问题。
但是,如果需要发送这个文件或数据长度,我们必须计算总长度。