Vishal Patel
Vishal Patel

Reputation: 2970

Retrofit @body with @multipart having Issue

Image Multipart in class type object.

case 1. (Which I had done)

Service params:

{"id":"1","name":"vishal","image/file":""} 

At that time my API of Retrofit

@Multipart
@POST("webservice")
Call<SignUpResp> loadSignupMultipart(@Part("description") RequestBody description, @Part MultipartBody.Part file, @QueryMap HashMap<String, String> params);

case 2. (Where I have Problem) with @Body class<UploadwithImage>

{
    "methodName":"submitLevel1Part2Icon",
    "userid":"150",
    "headerData":{
        "fiction":{
            "icon_type":"1",
            "icon_id":"3"},
        "nonfiction":{
            "icon_type":"2",
            "icon_id":"4"},
        "relation":{
            "icon_type":"3",
            "icon_id":"0",
            "name":"Ronak",
            "relative_image":"<File>",
            "relation_id":"3"},
        "self":{
            "icon_type":"4",
            "icon_id":"0"}
    }
}

I am trying this API

 @Multipart
 @POST("webservice")
 Call<SubmitLevel1Part2IconResp> loadLevel1halfIconswithImage(@Part("description") RequestBody description, @Part MultipartBody.Part file, @Body UploadwithImage uploadImage);

Java side

    /**
     * code for multipart
     */
     // create RequestBody instance from file
     RequestBody requestFile =  RequestBody.create(MediaType.parse("multipart/form-data"), fileUpload);

     // MultipartBody.Part is used to send also the actual filename
     MultipartBody.Part body =  MultipartBody.Part.createFormData("methodName[headerData][relation][relative_image]", fileUpload.getName(), requestFile);

     // add another part within the multipart request
     String descriptionString = "hello, this is description speaking";
     RequestBody description = RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString);

    call = service.loadLevel1halfIconswithImage(description, body, levelOneHalfIcons);

I don't know why but it returns error like:

"@Body parameters cannot be used with form or multi-part encoding"

Any Help would Be Appreciated.

Upvotes: 13

Views: 56489

Answers (8)

gpuser
gpuser

Reputation: 1183

We can add all request parameter in multipart body builder with specified type like in below one image file. I have set media type parse multipart/form-data and some other parameter I have set media type parse text/plain. This builder will build to make Multipart Body and can send by using body annotation in multipart body.

@Multipart
@POST("user/update")
Call<ResponseBody> addDocument(@@Part MultipartBody file);


final MultipartBody.Builder requestBodyBuilder = new MultipartBody.Builder()
      .setType(MultipartBody.FORM);

requestBodyBuilder.addFormDataPart("doc_image_file", imageFile.getName(),
      RequestBody.create(MediaType.parse("multipart/form-data"), imageFile));
requestBodyBuilder.addFormDataPart("user_id", null, RequestBody.create(MediaType.parse("text/plain"),"12"));
requestBodyBuilder.addFormDataPart("doc_name", null, RequestBody.create(MediaType.parse("text/plain"),"myfile"));
requestBodyBuilder.addFormDataPart("doc_category", null, RequestBody.create(MediaType.parse("text/plain"),category));
requestBodyBuilder.addFormDataPart("doc_image_file", imageFile.getName(),RequestBody.create(MediaType.parse("multipart/form-data"),imageFile));
requestBodyBuilder.addFormDataPart("doc_text_content", null, RequestBody.create(MediaType.parse("text/plain"),body));
RequestBody multipartBody = requestBodyBuilder.build();

Upvotes: 5

this works for me.

What I did was add every additional params using:

MultipartBody.Part Partname = MultipartBody.Part.createFormData("ParamName", "Value");

Mabe you don't need to create another body, but just add others params apart from the file or whatever you are sending. finally at the interface I put as a params every bodypart that I need.

@Multipart
@POST("api/service/uploadVideo")
Call<ResponseBody> uploadVideoToServer(
        @Part MultipartBody.Part video,
        @Part MultipartBody.Part param2,
        @Part MultipartBody.Part param3 ....
);

Upvotes: 0

sahan.perera
sahan.perera

Reputation: 31

https://www.linkedin.com/pulse/retrofit-2-how-upload-multiple-files-server-mahesh-gawale

I guess the best answer to this question can be found here. It worked perfectly for me.

This is the example of uploading an array of files using retrofit in Android.

This is how the service will look like

public interface ApiService {

    @POST("/event/store")
    Call<ResModel> event_store(@Body RequestBody file);
}

This is how the Client class look like

public class ApiClient   {
    public static final String API_BASE_URL = "api base url";

    private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();

    private static Retrofit.Builder builder = new Retrofit.Builder().baseUrl(API_BASE_URL).addConverterFactory(GsonConverterFactory.create());

    public static ApiService createService(Class<ApiService> serviceClass)
    {
        Retrofit retrofit = builder.client(httpClient.build()).build();
        return retrofit.create(serviceClass);
    }
}

Upload like this in activity or fragment or where you want

    ApiService service = ApiClient.createService(ApiService.class);

    MultipartBody.Builder builder = new MultipartBody.Builder();
    builder.setType(MultipartBody.FORM);


    builder.addFormDataPart("event_name", "xyz");
    builder.addFormDataPart("desc", "Lorem ipsum");

    // Single Image
    builder.addFormDataPart("files",file1.getName(),RequestBody.create(MediaType.parse("image/*"), file1));

    // Multiple Images 
    for (int i = 0; i <filePaths.size() ; i++) {
            File file = new File(filePaths.get(i));
            RequestBody requestImage = RequestBody.create(MediaType.parse("multipart/form-data"), file);
            builder.addFormDataPart("event_images[]", file.getName(), RequestBody.create(MediaType.parse("multipart/form-data"), file));
            }


    MultipartBody requestBody = builder.build();
    Call<ResModel> call = service.event_store(requestBody);
    call.enqueue(new Callback<ResponseBody>() {
         @Override
         public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
             Toast.makeText(getBaseContext(),"All fine",Toast.LENGTH_SHORT).show();
         }

         @Override
         public void onFailure(Call<ResponseBody> call, Throwable t) {
            Toast.makeText(getBaseContext(),t.getMessage(),Toast.LENGTH_SHORT).show();
         }
     });

Note: filePaths.size() is a Arraylist of pickup Images Paths. I hope this post is useful to you. kindly share your feedback as a comment here.

Upvotes: 0

Pratik Butani
Pratik Butani

Reputation: 62419

As simple way, I have done like this:

I have tested by changing

Call<Result> resultCall = service.uploadImage(body); 

to

Call<Result> resultCall = service.uploadImage(body, result); where result is

Result.java class (Response) of my API:

public class Result {

    @SerializedName("result")
    @Expose
    private String result;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    @SerializedName("value")
    @Expose
    private String value;

    /**
     * @return The result
     */
    public String getResult() {
        return result;
    }

    /**
     * @param result The result
     */
    public void setResult(String result) {
        this.result = result;
    }

}

and created object like:

Result result = new Result();
result.setResult("success");
result.setValue("my value");

You can change your class as per your need then pass object when you send request. So your ApiService class will be like:

ApiService.java

/**
 * @author Pratik Butani on 23/4/16.
 */
public interface ApiService {

    /*
    Retrofit get annotation with our URL
    And our method that will return us the List of Contacts
    */
    @Multipart
    @POST("upload.php")
    Call<Result> uploadImage(@Part MultipartBody.Part file, @Part("result") Result result);

}

and My PHP code is:

<?php

    $file_path = "";
    $var = $_POST['result']; //here I m getting JSON

    $file_path = $file_path . basename( $_FILES['uploaded_file']['name']);
    if(move_uploaded_file($_FILES['uploaded_file']['tmp_name'], $file_path)) {
        $result = array("result" => "success", "value" => $var);
    } else{
        $result = array("result" => "error");
    }

    echo json_encode($result);

?>

Hope it will helps you. Thank you.

Upvotes: 11

gpuser
gpuser

Reputation: 1183

You can also use a Map with RequestBody as value and string as keys to add parameters and you can send this using Multipart and PartMap.

Check the following code, hope it will help :

@Multipart
@POST("/add")
Call<ResponseBody> addDocument(@PartMap Map<String,RequestBody> params);

Map<String, RequestBody> map = new HashMap<>();

map.put("user_id", RequestBody.create(MediaType.parse("multipart/form-data"), SessionManager.getInstance().getCurrentUserId()));
map.put("doc_name", RequestBody.create(MediaType.parse("multipart/form-data"), CommonUtils.removeExtension(textFile.getName())));
map.put("doc_category", RequestBody.create(MediaType.parse("multipart/form-data"), category));
map.put("doc_image_file", RequestBody.create(MediaType.parse("multipart/form-data"), imageFile));
map.put("doc_text_content", RequestBody.create(MediaType.parse("multipart/form-data"), body));
map.put("doc_update_time", RequestBody.create(MediaType.parse("multipart/form-data"), "" + new Date(textFile.lastModified())));

Upvotes: 7

Jatin
Jatin

Reputation: 1670

Here is my json request format is :
{
"task":{
"category_id":"1",
"price":"10",
"description":"1",
"task_videos_attributes":[
{
"link":"video file goes here",
"size":"100x100"
}
]
}
}



// my request becomes 
 HashMap<String, RequestBody> task = new HashMap();          
  task.put("task[category_id]", createPartFromString(categoryId));
  task.put("task[price]", createPartFromString("" + etPrice.getText().toString()));
            task.put("task[description]", createPartFromString("" + etDescription.getText().toString()));


// for videos file list
  final List<MultipartBody.Part> body = new ArrayList<>();
  for (int i = 0; i < videos.size(); i++) {

 task.put("task[task_videos_attributes][" + i+ "][size]", createPartFromString("100x100"));

 File videoFile = new File(videos.get(i));
                        RequestBody requestBody = RequestBody.create(MediaType.parse("video/mp4"), videoFile);
                        MultipartBody.Part fileToUpload = MultipartBody.Part.createFormData("task[task_videos_attributes][" + i + "][link]", videoFile.getName(), requestBody);
                        body.add(fileToUpload);

}


// here is a final call
  new RestClient(this).getInstance().get().postTask(body, task).enqueue(callback);




// This function converts my string to request body
   @NonNull
    private RequestBody createPartFromString(String descriptionString) {
        if (descriptionString == null)
            return RequestBody.create(MultipartBody.FORM, "");
        return RequestBody.create(
                MultipartBody.FORM, descriptionString);

    }

Hope this helps you...

Upvotes: 2

andylee
andylee

Reputation: 433

Just follow how the web browser is doing multipart. They put nested keys in "[]" and give key to the images too.

Call<SubmitLevel1Part2IconResp> loadLevel1halfIconswithImage(@Part("headerdata[relation][icon_type]") RequestBody icon_type, @Part("headerdata[relation][name]") RequestBody name, @Part MultipartBody.Part file);

And then in java

 // MultipartBody.Part is used to send also the actual filename
 MultipartBody.Part body =  MultipartBody.Part.createFormData("headerdata[relation][relative_image]", fileUpload.getName(), requestFile);



call = service.loadLevel1halfIconswithImage(icon_type, name, body);

Upvotes: 0

Ankit Aggarwal
Ankit Aggarwal

Reputation: 5375

Change your method to

@Multipart
@POST("users/{id}/user_photos")
Call<models.UploadResponse> uploadPhoto(@Path("id") int userId, @PartMap Map<String, RequestBody> params);

Now to create your request parameters,

//All the String parameters, you have to put like
Map<String, RequestBody> map = new HashMap<>();
map.put("methodName", toRequestBody(methodName));
map.put("userid", toRequestBody(userId));
map.put("relation", toRequestBody(relation));
map.put("icon_type", toRequestBody(iconType));
map.put("icon_id", toRequestBody(iconId));
map.put("name", toRequestBody(name));
map.put("relation_id", toRequestBody(relationId));

//To put your image file you have to do
File file = new File("file_name");
RequestBody fileBody = RequestBody.create(MediaType.parse("image/png"), file);
map.put("relative_image\"; filename=\"some_file_name.png\"", fileBody);

// This method  converts String to RequestBody
public static RequestBody toRequestBody (String value) {
    RequestBody body = RequestBody.create(MediaType.parse("text/plain"), value);
    return body ;
}

//To send your request
call = service.loadLevel1halfIconswithImage(description, params);

In case you do not want to use PartMap, you can simply pass them as parameters. Check my answer https://stackoverflow.com/a/37052548/1320616 to get some clue on sending image file with request.

Upvotes: 15

Related Questions