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.
4- Click in Send and then Generate code.
5- Now you have something like following:
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
Finish an Activity After a Time Period
Enable C++11 Support on Android
Couldn't Get Connection Factory Client - Fighting with Google Maps
Android 4.3 and Phonegap, Cant Tap Link
How to Compile Ffmpeg-2.2.2 on Windows with Cygwin and Android Ndk R9C
Android: Using HTML5 to Determine Geolocation in Webview with JavaScript API
Mysterious Stacktrace in Android Developer Console (Bitmap Size Exceeds 32Bits)
Android in Navigation Drawer When I Click Anywhere Drawer Closes
Parsing Date/Time to Localtimezone
Getting Error "Gradle Dsl Method Not Found: 'Compile()'" When Syncing Build.Gradle
Android Dialog: Removing Title Bar
Android Webview HTML5 Video Autoplay Not Working on Android 4.0.3
How to Handle Empty Response Body with Retrofit 2
How to Replace a Fragment on Button Click of That Fragment
Error Building Android Library: Direct Local .Aar File Dependencies Are Not Supported
Failed to Find Style 'Mapviewstyle' in Current Theme
Fragment Must Be a Public Static Class to Be Properly Recreated from Instance State