Send File to Server via Retrofit2 as Object

Send file to server via retrofit2 as object

For finding How to send your file to Server via Retroit following steps may solve your problem:

1- Install PostMan.

2- In PostMan select Post and paste URL then go to Body tab and choose form-data.

3- In Key's part write server file name and In Value's part set type as File and upload desire file.

Sample Image

4- Click in Send and then Generate code.

5- Now you have something like following:

Sample Image

6- Now just one step remain go to your retrofit service and paste info like (In my case I want to upload audio.mp3) :

    @Multipart
@POST("app/")
Call<JResponse> upload(@Part("file\"; filename=\"audio.mp3\" ") RequestBody file);

And request body would be something like:

File file = new File("YOUR_PATH");
RequestBody temp = RequestBody.create(MediaType.parse("multipart/form-data"), file);

Use this pattern and send it with:

 ServiceHelper.getInstance().sendAudio(temp).enqueue(new Callback<JResponse>() {
@Override
public void onResponse(Call<JResponse> call, Response<JResponse> response) {
Log.e("test", "onResponse: tst");

}

@Override
public void onFailure(Call<JResponse> call, Throwable t) {
Log.e("test", "onResponse: tst");

}
});

how to use retrofit 2 to send file and other params together

Use gson and create a model class for the location.

Add the following dependencies to your build.gradle.

compile 'com.squareup.retrofit2:converter-gson:2.0.0'
compile 'com.google.code.gson:gson:2.5'

Create a model to represent the location.

public class Location {

double lat;
double lng;
String location;

public Location(double lat, double lon, String place) {
this.lat = lat;
this.lon = long;
this.place = place;
}

}

If the variable names for the payload fields don't match the actual required name for the endpoint you will need to add the annotation @SerializedName([expected name])

ex:

import com.google.gson.annotations.SerializedName;

public class Location {

@SerializedName("lat")
double latitude;
@SerializedName("lng")
double longitude;
@SerializedName("place")
String location;

public Location(double lat, double lon, String place) {
latitude = lat;
longitude = long;
location = place;
}

}

Define the api interface.

public interface Api {

@POST("upload/")
@Multipart
Call<ResponseBody> uploadFile(@Part("title") RequestBody title,
@Part MultipartBody.Part imageFile,
@Part("location") Location location
);

}

Create a Retrofit instance and call the api.

File file;
// create retrofit instance
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://baseurl.com/api/")
.addConverterFactory(GsonConverterFactory.create())
.build();
// create api instance
Api api = retrofit.create(Api.class);
// create call object
Call<ResponseBody> uploadFileCall = api.uploadFile(
RequestBody.create(MediaType.parse("text/plain"), "title"),
MultipartBody.Part.createFormData(
"file",
file.getName(),
RequestBody.create(MediaType.parse("image"), file)),
new Location(48.8583, 2.29232, "Eiffel Tower"));
// sync call
try {
ResponseBody responseBody = uploadFileCall.execute().body();
} catch (IOException e) {
e.printStackTrace();
}
// async call
uploadFileCall.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
// TODO
}
}

@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
// TODO
}
});

You will need to change the MediaType.parse() call if you are not using an image file.

You can similarly create a custom response type object and replace ResponseBody with it to receive a deserialized result object.

Let me know if this works. I didn't have a chance to test in your exact scenario obviously but I'm fairly confident this should work. The only part I'm not 100% on is whether @Part("location") Location location should be @Body("location") Location location

Send json and file with Retrofit2

for sending json and file you can follow something like this.

@Multipart
@POST("goals")
Call<JsonModel> postGoal(@Part MultipartBody.Part file, @Part("json") RequestBody json);

Now convert your Object which you want to send as a json into json using Gson.
like this.

    String json = new Gson().toJson(new Goal());

File file = new File(path);
RequestBody requestFile =
RequestBody.create(MediaType.parse("multipart/form-data"), file);

// MultipartBody.Part is used to send also the actual file name
MultipartBody.Part body =
MultipartBody.Part.createFormData("picture", file.getName(), requestFile);

// add another part within the multipart request
RequestBody jsonBody=
RequestBody.create(
MediaType.parse("multipart/form-data"), json);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
RestApi api = retrofit.create(RestApi.class);
Call<ResponseBody> call = api.upload(jsonBody, body);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d("onResponse: ", "success");
}

@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.d("onFailure: ", t.getLocalizedMessage());
}
});

Upload file in Retrofit 2

The following code worked :)

 @Multipart
@POST("myrecord")
Call<ResponseBody> addRecord(@Query("token") String token, @Query("userid") int userId,
@Query("name") String name, @Part MultipartBody.Part file);

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if ((requestCode == FILE_SELECT_CODE) && (resultCode == -1)) {

File file = new File(getRealPathFromURI(data.getData()));

RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), getRealPathFromURI(data.getData()));

MultipartBody.Part multipartBody =MultipartBody.Part.createFormData("file",file.getName(),requestFile);

Call<ResponseBody> responseBodyCall = service.addRecord(token, userId, "fileName", multipartBody);
responseBodyCall.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d("Success", "success "+response.code());
Log.d("Success", "success "+response.message());

}

@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.d("failure", "message = " + t.getMessage());
Log.d("failure", "cause = " + t.getCause());
}
});

}
}

Uploading a file in an array of object using Retrofit 2

Solution 1

If you like to send your data exactly like the structure you mentioned, you should convert files content to Base64 and wrap them in a serializable class and post it as the body. Here is the sample of wrapper class:

data class AnswerExerciceBase64(val state: String, val medias: List<Media>) : Serializable

data class Media(val file: Base64File) : Serializable

class Base64File(file: File) : Serializable {

val name: String
val content: String

init {
name = file.name
content = Base64.encodeToString(FileInputStream(file).readBytes(), Base64.DEFAULT)
}
}

And your Api is like this:

@POST("api/exercice/{id}")
fun submitExercice(
@Path("id") id: Int,
@Header("Authorization") token: String,
@Body data: AnswerExerciceBase64
): Call<Void>

Then posted data to server will be like below:

{
"state": "this is state",
"medias": [{
"file": {
"content": "Base64 file content",
"name": "f1.txt"
}
}, {
"file": {
"content": "Base64 file content",
"name": "f2.txt"
}
}, {
"file": {
"content": "Base64 file content",
"name": "f3.txt"
}
}]
}

This approach is so close to what you want but you should know you must decode files content on the server-side by yourself, so you need more effort on the server-side.



Solution 2

It's better to use multipart/form-data to upload files and data. Based on "Is it possible to have a nested MultipartEntities or FormBodyPart in a multipart POST?" question and its answer, multipart/form-data has a flat structure and there is no hierarchy, so you can't have desired data structure but you can still pass all of the inputs to Api through a single object.

According to this article, you can send multiple files in a List, so if your Api be like this

@Multipart
@POST("post")
fun submitExercice(@Part data: List<MultipartBody.Part>): Call<ResponseBody>

then you will be able to upload multiple files. You just need to create a List of MultipartBody.Part and add your files to it like below:

list.add(MultipartBody.Part.createFormData(name, fileName, RequestBody.create(mediaType, file)))

Now you must add the state parameter to this list. You can do it like this:

list.add(MultipartBody.Part.createFormData("state", state))

I developed a class that handles all this stuff. You can use it.

class AnswerExerciceList(state: String) : ArrayList<MultipartBody.Part>() {

init {
add(MultipartBody.Part.createFormData("state", state))
}

fun addFile(name: String, fileName: String, mediaType: MediaType?, file: File) {
add(MultipartBody.Part.createFormData(name, fileName,
RequestBody.create(mediaType, file)))
}
}

You can create an instance of this class, add your files and then pass it to the submitExercice Api method as input.



Update

This answer is based on your Api documnetation. I tested my answer and the example that you mentioned in your question via https://postman-echo.com and result was the same. Please try the following code snippet:
Api

@Multipart
@POST("api/exercice/{id}")
fun submitExercice(@Path("id") id: Int,
@Header("Authorization") authorization: String,
@Part("answer") answer: String,
@Part medias: List<MultipartBody.Part>,
@Part("state") state: String): Call<ResponseBody>

Media Class

data class Media(val urlVidel: String, val file: File?, val mediaType: MediaType?) {
companion object {
fun mediaListToMultipart(mediaList: List<Media>): List<MultipartBody.Part> {
val list = ArrayList<MultipartBody.Part>()
for (i in mediaList.indices) {
mediaList[i].let {
if (!TextUtils.isEmpty(it.urlVidel))
list.add(MultipartBody.Part.createFormData("medias[$i][urlVideo]", it.urlVidel))
if (it.file != null) {
val requestFile = RequestBody.create(
it.mediaType,
it.file
)
list.add(MultipartBody.Part.createFormData("medias[$i][file]", it.file.getName(), requestFile))
}
}
}
return list
}
}
}

and then call Api like this:

ApiHelper.Instance.submitExercice(1, "Authorization Token", "Answer", Media.mediaListToMultipart(mediaList), "State").enqueue(callback)


Related Topics



Leave a reply



Submit