Retrofit. java.net.ProtocolException: expected * bytes but received *

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<>();

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.


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.


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;