Upload files with retrofit - java

I tried work with API
My API
And Java code
public interface UploadAPI {
#Multipart
#POST("books")
Call<AddBookResult> uploadBook(
#Part MultipartBody.Part audios,
#Part("image") RequestBody image,
#Part("audio_names") List<String> audio_names,
#Part("video_names") List<String> video_names,
#Part("video_urls") List<String> video_urls,
#Part("preview") RequestBody preview,
#Part("hot") String hotParam,
#Part("new") String newParam,
#Part("coming") String comingParam,
#Part("sale_offs") List<String> sale_offs,
#Part("author") String author,
#Part("publisher") String publisher,
#Part("categories") List<String> categories,
#Part("name") String name,
#Part("price") String price
);
}
Call API
Code Java Call API
And result of respone
Internal Server Error
:(

First of all internal server error (status code 500) means that you have some kind of issue with your backend or your API cannot find the path to the called endpoint controller.
Im not sure how is your backend configured, but i think it may be the path problem, try adding the "/" before books in request declaration like this
public interface UploadAPI {
#Multipart
#POST("/books") //here you have to put path to your controller
Call<AddBookResult> uploadBook(
#Part MultipartBody.Part audios,
#Part("image") RequestBody image,
#Part("audio_names") List<String> audio_names,
#Part("video_names") List<String> video_names,
#Part("video_urls") List<String> video_urls,
#Part("preview") RequestBody preview,
#Part("hot") String hotParam,
#Part("new") String newParam,
#Part("coming") String comingParam,
#Part("sale_offs") List<String> sale_offs,
#Part("author") String author,
#Part("publisher") String publisher,
#Part("categories") List<String> categories,
#Part("name") String name,
#Part("price") String price
);
}
If this doesn't solve your problem, post the detailed log of your request, or take a look on this gist if you want full working example of image upload with progress bar

Related

Retrofit Post Multipart form data values are empty

I am trying to post some multipart form data via Retrofit in Android to a web service.
The web services's API expects the following parameters to be passed as field of a multipart form data:
Name Type
company_id text/plain
image image/*
It also expects an authorization token to be passed as a query string parameter.
So i have defined my API interface call like this:
#Multipart
#POST("/companies/uploadImage")
#Headers({
"Content-Type: multipart/form-data",
"Accept: application/json"
})
Call<ServerResponse> companyUploadImage( #Part("company_id") RequestBody companyId, #Part MultipartBody.Part file, #Query("token") String token);
Afterwards this is how i call the above API interface method in a custom class:
RequestBody companyId = RequestBody.create(MediaType.parse("text/plain"), LocalStorage.getInstance().getCompanyId());
File file = new File(postPath);
MultipartBody.Part image = MultipartBody.Part.createFormData("image", file.getName(), RequestBody.create(MediaType.parse("image/*"), file));
Call<ServerResponse> uploadProfileImage = router.companyUploadImage(companyId, image, token);
uploadProfileImage.enqueue(new Callback<ServerResponse>() {
#Override
public void onResponse(Call<ServerResponse> call, Response<ServerResponse> response) {
Log.e("Upload Profile Image: ", response.body().getMessage());
}
#Override
public void onFailure(Call<ServerResponse> call, Throwable t) {
Log.e("Upload Profile Image Error: ", t.getMessage());
}
});
The request is sent successfully to the server, so no networking exceptions occur, however the multipart form values, company_id and image, are received empty on the server side
Any idea?
Thank you!
I managed to solve the issue. It seems that the solution was as simple as removing the #Header annotation from the API interface method. So now it looks like this:
#Multipart
#POST("/companies/uploadImage")
Call<ServerResponse> companyUploadImage( #Part("company_id") RequestBody companyId, #Part MultipartBody.Part file, #Query("token") String token);
Maybe someone will find this post helpful.

Upload a file to AWS S3 pre-signed URL using Retrofit2

I'm trying to upload a file to Amazon's S3 using a pre-signed URL. I get the URL from a server which generates the URL & sends it to me as part of a JSON object. I get the URL as a String, something like this:
https://com-example-mysite.s3-us-east-1.amazonaws.com/userFolder/ImageName?X-Amz-Security-Token=xxfooxx%2F%2F%2F%2F%2F%2F%2F%2F%2F%2Fxxbarxx%3D&X-Amz-Algorithm=xxAlgoxx&X-Amz-Date=20170831T090152Z&X-Amz-SignedHeaders=host&X-Amz-Expires=3600&X-Amz-Credential=xxcredxx&X-Amz-Signature=xxsignxx
Unfortunately, when I pass this to Retrofit2, it modifies the String attempting to make it into a URL. I've set encoding=true which took care of most of the problem but not completely. I know the String works as it is. I've tried it in Postman & get a successful response.
1st I tried just putting the String (except for what I cut out as baseUrl) as a whole into the Path
public interface UpdateImageInterface {
#PUT("{url}")
Call<Void> updateImage(#Path(value="url", encoded=true) String url, Body RequestBody image);
}
The calling code:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://com-example-mysite.s3-us-east-1.amazonaws.com/userFolder/")
.build();
UpdateImageInterface imageInterface = retrofit.create(UpdateImageInterface.class);
// imageUrl is "ImageName..."
Call<Void> call = imageInterface.updateImage(imageUrl, requestFile);
This works mostly except the the '?' (after "ImageName") get converted to "%3F". This causes a Bad Request / 400.
My next attempt was to create a query with Retrofit2 but then dump the whole String (with multiple queries) into the query.
public interface UpdateImageInterface {
#PUT("ImageName")
Call<Void> updateProfilePhoto(#Query(value="X-Amz-Security-Token", encoded = true) String token, #Body RequestBody image);
}
The calling code:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://com-example-mysite.s3-us-east-1.amazonaws.com/userFolder/")
.build();
UpdateImageInterface imageInterface = retrofit.create(UpdateImageInterface.class);
// imageUrl is "xxfooxx..."
Call<Void> call = imageInterface.updateImage(imageUrl, requestFile);
This gets the '?' rendered correctly but all of the '&' get changed to "%26"
Lastly I tried passing the whole String in baseUrl() but that gives an IllegalArgumentException for not having '/' on the end.
I know that I could parse the pre-signed URL to make multiple queries & assemble them in Retrofit2 as queries should be done but I'd like to avoid that processing.
To restate the question:
Is there a way to easily (without heavy String parsing) upload a file to S3 with a pre-signed URL using Retrofit2?
With help from a colleague, this is the solution.
public interface UpdateImageInterface {
#PUT
Call<Void> updateImage(#Url String url, #Body RequestBody image);
}
Calling code:
String CONTENT_IMAGE = "image/jpeg";
File file = new File(localPhotoPath); // create new file on device
RequestBody requestFile = RequestBody.create(MediaType.parse(CONTENT_IMAGE), file);
/* since the pre-signed URL from S3 contains a host, this dummy URL will
* be replaced completely by the pre-signed URL. (I'm using baseURl(String) here
* but see baseUrl(okhttp3.HttpUrl) in Javadoc for how base URLs are handled
*/
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.dummy.com/")
.build();
UpdateImageInterface imageInterface = retrofit.create(UpdateImageInterface.class);
// imageUrl is the String as received from AWS S3
Call<Void> call = imageInterface.updateImage(imageUrl, requestFile);
Javadoc for info on #Url (class Url) &
baseUrl() (class Retrofit.Builder)
MediaType is a class in the OkHttp library that is often used with Retrofit (both from Square). Info about constants passed to the parse method can be found in the Javadoc.
Use the following while uploading directly to S3 using presigned URL.
#Multipart
#PUT
#Headers("x-amz-acl:public-read")
Call<Void> uploadFile(#Url String url, #Header("Content-Type") String contentType, #Part MultipartBody.Part part);

Retrofit multipart request

I am using retrofit2+rxAndroid to work with REST. I want to send multipart request with image and text. But something is not working and instead of image+ text I have this "trim,nshtml,max|140".
This is my request:
#Multipart
#POST("feed/post/add")
public Observable<VehicleSearchResponse>
addPost(#Header("Authorization") String token, #Header("Content-Type") String contenttype,
#Part("text") String message,
#Part("attached_images") RequestBody file);
And this is how I form my RequestBody
RequestBody.create(MediaType.parse("image/jpg"), persistImage(bitmap2, "test"))
Please help me.
Have you tried using:
MultipartBody.create(MultipartBody.FORM, bytes)
for the attached_images part?

How can I send a POJO object in a Multipart-form Request using Retrofit?

I am trying to send the following in a Multipart-form request using Retrofit:
{ "attachment": {
"file_cache": "....."
"original": "upload/image.png",
"versions": {
"small": "uploads/small_image.png"
}
},
"content": "",
"id": 1
}
I don't know if this is a correct request I should be sending to the API, since their documentation is really horrible but I was able to use Chrome Dev Tools to study what the API was receiving request wise and how it was responding, it seems to accept that JSON.
Here is a photo of what I observed:
Their documentation only states that "attachment" should be an object.
Is it possible at all to send a POJO object in a multipart-form request? My REST Interface looks like this:
#Multipart
#POST("/v2/{type}/{id}/message.json")
void addMessage(#Path("type") String type,
#Path("id") int id,
#Part("content") String content,
#Part("attachment") MultipartTypedOutput multipartTypedOutput,
Callback<Post> callback);
Sending a MultipartTypedOutput didn't work, neither did using the following:
addMessage(...., #Part("attachment") POJOObject object, ...);
Any ideas on how to accomplish this?
I get a status 422 un-processable entity if I try to send a POJO object with Retrofit.
I was able to solve this by following this link here.
I was not aware of this but in order to send JSON you need to setup your REST API Service like so:
#Multipart
#POST("/v2/{type}/{id}/message.json")
void addMessage(#Path("type") String type,
#Path("id") int id,
#Part("content") String content,
#Part("attachment[file_cache]") String fileCache,
#Part("attachment[original]") String original,
#Part("attachment[versions][small]") String small,
Callback<Post> callback);
Hopefully this helps someone else out down the road.
Have you tried TypedFile ? If not try this way,
#Multipart
#POST("/v2/{type}/{id}/message.json")
void addMessage(#Path("type") String type,
#Path("id") int id,
#Part("content") String content,
#Part("attachment") TypedFile photoFile,
Callback<Post> callback);
For TypedFile you can pass attachment this way,
File attachmentFile = new File("upload/image.png");
TypedFile attachmentTypedFile = new TypedFile("image/*", photoFile);

Spring RestTemplate : get with nullable paramaeter

I'm trying to make a true RestFull service and keeping to the documentation. However I'm stuck now with a problem I can't see a clear answer for. I want to use a filter to query some data from the webservice. The following path is defined on the controller of the webservice
#RequestMapping(value="/rest/postalcode/list/filter?lang={lang}&postalcode={postalcode}&country={country}&city={city}", method = RequestMethod.GET, produces="application/json")
public #ResponseBody JsonPostalCodeList findFilteredPostalCodes(#PathVariable("lang") String lang, #PathVariable("postalcode") String postalcode, #PathVariable("country") Long country, #PathVariable("city") String city, Model model) throws Exception {
}
Then I try to call it with the following method on client side
public JsonPostalCodeList findPostalCodes(
JsonPostalCodeSelectorData selectorData) {
String url = getWebserviceLocation()+"/rest/postalcode/list/filter?lang={lang}&postalcode={postalcode}&country={country}&city={city}";
MbaLog.debugLog(logger,"Calling webservice with url: " + url);
return getRestTemplate().getForObject(url, JsonPostalCodeList.class, selectorData.getContactLanguage(), selectorData.getPostalCode(), selectorData.getCountry(), selectorData.getCity());
}
now selectorData.getPostalCode() can be null for example because , the user didn't fill in a postalcode to filter on. Same can be true for country and city (lang is always filled in). But each time I run it I get an IOException not found (probably due to the null's). I tried once with everything filled in and I go perfectly in my method at service side. So how do you handle such a problem ?
I can solve it by throwing GET out of the window and just put everything in a POST body as a JSONobject mapped with Jackson and problem solved. But then I'm using a POST to fetch data while a GET should be used in pure REST to fetch data.
So RestTemplate and querying services with variable data how to go about it ?
Just took a cold shower and found it out myself :)
I don't have to use pathvariables I can just use request parameters.
#RequestMapping(value="/rest/postalcode/list/filter", method = RequestMethod.GET, produces="application/json")
public #ResponseBody JsonPostalCodeList findFilteredPostalCodes(#RequestParam("lang") String lang, #RequestParam("postalcode") String postalcode, #RequestParam("country") Long country, #RequestParam("city") String city, Model model) throws Exception {
}
and calling it with
#Override
public JsonPostalCodeList findPostalCodes(
JsonPostalCodeSelectorData selectorData) {
String url = getWebserviceLocation()+"/rest/postalcode/list/filter?lang={lang}&postalcode={postalcode}&country={country}&city={city}";
MbaLog.debugLog(logger,"Calling webservice with url: " + url);
return getRestTemplate().getForObject(url, JsonPostalCodeList.class, selectorData.getContactLanguage(), selectorData.getPostalCode(), selectorData.getCountry(), selectorData.getCity());
}

Categories

Resources