Reputation: 4463
I'm trying to do a Multipart POST request through Retrofit2, where I upload to the API a custom file.
It randomly fails with this Exception:
W/System.err: java.net.ProtocolException: expected 154 bytes but received 634
Does anyone could put some light on it?
This is my code in the interface:
@Multipart
@POST("recordings/{id}/{rec_id}/")
Call<ResponseBody> uploadRecording(@Path("id") String id, @Path("rec_id") String rec_id, @Part MultipartBody.Part bleFile);
In the constructor:
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);
}
and in the upload method:
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();
}
});
}
Upvotes: 12
Views: 10794
Reputation: 1819
In my case, I used HttpUrlConnection
for Uploading File/Bitmap/Buffer as Multi-Part protocol uploading...
short Answer :
I removed following line in my code and every things worked fine :
// remove this line
connection.setFixedLengthStreamingMode(dataToUpload.length);
long Answer :
for multi-part upload we must write some extra data such as boundary
, twoHyphens (--)
, newLine (\r\n)
... Therefore, the length of the data will be longer than the length of the data(file/bitmap/buffered).
By deleting the setFixedLengthStreamingMode
in connection request, We can solves this problem.
But, If it is necessary to send this file or data length, we must calculate the total length.
Upvotes: 0
Reputation: 2719
It means that file what you're trying to send is still creating and it's size changing. You should finalize your file first and then send.
Upvotes: 0
Reputation: 3258
I have encountered the same issue and solved by creating a temporary file before upload it.
In 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
}
And after upload finished, I deleted the temporary file.
Upvotes: 2
Reputation: 441
I faced this problem when I was trying to upload a recording. I solved it by stopping the recording process before calling the web service to upload the file.
objMediaRecorder.stop();
objMediaRecorder.release();
objMediaRecorder = null;
Upvotes: 4
Reputation: 4463
After studying for a while the problem I've realized that the content of the file was always changing (as it is the output of a sensor).
It means that the file that is checked for the HEAD and the file for the BODY may not contain the same data (therefore different length), which caused the mismatch.
I solved this creating a copy of the file and sending it (the copy) instead of the original file.
Upvotes: 4
Reputation:
Here is what I use for all my requests and have no problems at all. Let me know if it does not work for you. I am assuming POST name for your file is "file".
In the protocol:
@Multipart
@POST
Call<ResponseBody> request(
@Url String url, // Request URL
@PartMap Map<String, String> vars, // POST Strings
@PartMap Map<String, RequestBody> files // POST Files
);
Construct a call:
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: You can use this piece of code to upload multiple files, since it accepts a map of files rather than a single file.
PS#2: Due to several problems I have had with this method, I had to add the below code to make sure it never sends null or empty map.
if (vars == null) {
vars = new HashMap<>();
vars.put("01110011", "01101101"); // put whatever you want
}
if (files == null) files = new HashMap<>();
Upvotes: 1