I have an image on a server stored in a file (not db). I want to display this image in a layout view in Android Studio. I am using Web API2 and retrofit to exchange data.
Using retrofit, I know I need to send the file encapsulated in a class. I am not aware of what type to create? (Byte array?) and how retrofit on the android side would convert this type. I have tried to use byte[] on boths side however retrofit was not able to read the byte[] from Json.
Would anyone be able to guide me on how I would go about transferring this jpeg image? Thanks!
I've recently implemented it.
This is the method in my api.
[HttpPost]
public IHttpActionResult Upload()
{
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
// Do something with file.
}
else
{
// No files.
}
}
The class you are looking for is retrofit's TypedFile class.
This is my implementation.
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint("baseurl").build();
ApiTaqueria api = restAdapter.create(ApiTaqueria.class);
TypedFile foto = new TypedFile("multipart/form-data", new File("path"));
api.subirLogoTaqueria(foto, new Callback<Taqueria>() {
#Override
public void success(Taqueria result, Response response) {
// Do something.
}
#Override
public void failure(RetrofitError retrofitError) {
// Do something.
}
});
In the retrofit interface.
#Multipart
#POST("/api/Photo/Upload")
public void subirLogoTaqueria(#Part("foto") TypedFile foto, Callback<Taqueria> callback);
Happy coding.
Related
I am trying to get the CompanyEndpoint for each client's site but I am confused with the use of retrofit on the interface.
Here's what I have so far:
CompanyName : "company1"
CompanyEndpoint : "https://example.com"
IdentityEndpoint : "https://example.com/identity"
AppLoginMode : "Anonymous"
AppRouterApi.java
public interface AppRouterApi {
#GET("api/sites/{CompanyName}")
Call<Company> getCompanyName (#Url String companyName);
}
Company.java
public class Company {
String Endpoint;
public String getEndpoint() {
return endpoint;
}
}
MainActivity.java
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
appRouterApi = retrofit.create(AppRouterApi.class);
getCompany();
}
private void getCompany(){
retrofit2.Call<Company> companyRequest = appRouterApi.getCompanyName(); //Error here saying a string cant be applied to ()
companyRequest.enqueue(new retrofit2.Callback<Company>() {
#Override
public void onResponse(retrofit2.Call<Company> call, retrofit2.Response<Company> response) {
if(!response.isSuccessful()){
textViewResult.setText("Code:" + response.code());
return;
}
Company company = response.body();
String content = "";
content += "Url" + company.getEndpoint();
textViewResult.setText(content);
}
#Override
public void onFailure(retrofit2.Call<Company> call, Throwable t) {
}
});
}
https://example/sites/{companyName}
So if I search for:
https://example/sites/company1
The JSON will have one object and I need to get the endpoint URL value which would be: https://company1.com
Edit: My textViewReslt is returning 403
There are several things going on as far as I can tell. Let me break it into chunks.
First thing is you're confusing the annotation #Path with the annotation #Url. They serve different purposes.
You use #Path when you want to format a bit of the path into the url inside the annotations like #GET.
public interface AppRouterApi {
#GET("api/sites/{CompanyName}")
Call<Company> getCompanyName (#Path("CompanyName") String companyName);
}
This interface will format the argument passed to getCompanyName as part of the path. Calling getCompanyName("foo") will call the endpoint "https://example.com/api/sites/foo".
You use #Url when you want to simply call that url. In this case, you only annotate the interface method with the http method. For example,
public interface AppRouterApi {
#GET
Call<Company> getCompanyName (#Url String url);
}
You then would have to call the method with the entire url. To call the same url as before you'd have to call getCompanyName("https://example.com/api/sites/foo").
This is the main difference of usage between these 2 annotations. The reason why you're seeing null in your text view is because you're model's attribute name doesn't match the json. You have 2 options.
First, you can change the model to:
public class Company {
String CompanyEndpoint;
public String getEndpoint() {
return endpoint;
}
}
CompanyEndpoint is the exact same name as you have in the json. Another approach, is to tell your json serializer what name you want to use. Since you're using gson, you can use #SerializedName like so:
public class Company {
#SerializedName("CompanyEndpoint")
String Endpoint;
public String getEndpoint() {
return endpoint;
}
}
#SerializedName("CompanyEndpoint") tells gson which name to use while serializing and deserializing.
In essence, you have 2 options. You either use the endpoint, or the company's name. If you don't expect the domain to change, I'd suggest using the first approach with the #Path annotation. This is what it's usually done with Retrofit and personally, I think it's easier to handle than passing urls around. My suggestion is, use a model like:
public class Company {
#SerializedName("CompanyName")
String name;
public String getName() {
return name;
}
}
This would let you access the company's name property and call getCompanyName(company.getName()). Retrofit would format the company's name into the path and you'd call the right url.
I'm trying to build my URL using Retrofit 2.0. The problem is it's returning this URL:
http://query.yahooapis.com/v1/public/yql?&q=select%20*%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(%22YHOO%22)&format=json%26diagnostics%3Dtrue%26env%3Dstore%253A%252F%252Fdatatables.org%252Falltableswithkeys%26callback%3D
I want it to return this URL instead:
https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(%22YHOO%22)&format=json&diagnostics=true&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=
Can anyone please advise how do I fix this?
Here is the code that returns the URL:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
mQuoteAdapter = new QuoteAdapter(items);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.question_list);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(mQuoteAdapter);
StockApiServiceInterface stockApiServiceInterface = retrofit.create(StockApiServiceInterface.class);
stockApiServiceInterface.listQuotes(
"select * from yahoo.finance.quotes where symbol in (\"YHOO\")",
"json&diagnostics=true&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=")
.enqueue(new Callback<ResultWrapper>() {
#Override
public void onResponse(Response<ResultWrapper> response) {
response.body().getQuery().getResults().getQuote().getAsk();
}
#Override
public void onFailure(Throwable t) {
Log.e("listQuotes threw: ", t.getMessage());
}
});
Here is my StockApiService:
public final class StockApiService {
public interface StockApiServiceInterface {
#GET("v1/public/yql?")
Call<ResultWrapper> listQuotes(
#Query("q") String query,
#Query("format") String env
);
}
}
Remove the question-mark form your request URL like this:
#GET("v1/public/yql")
And seperate the parameters you are sending here:
"json&diagnostics=true&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=")
Into query items.
Your method should look something like this:
#Query("q") String query,
#Query("format") String format,
#Query("diagnostics") boolean diagnostics,
#Query("env") String enviroment,
#Query("callback") boolean callback
Slight change from Ian that simplifies it a little:
public final class StockApiService {
public interface StockApiServiceInterface {
#GET("v1/public/yql?format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys")
Call<ResultWrapper> listQuotes(
#Query("q") String query,
#Query("diagnostics") boolean diagostics
);
}
}
Unchanging query strings parameters can be included in the method annotation and retrofit should merge them together. Also, I removed the callback parameter because that is a thing for websites called JSONP and isn't relevant to an Android app.
The actual problem you have is that you are giving Retrofit a precomposed partial query string and asking it to encode it for you. Retrofit doesn't know that it's a precomposed query string, so it does what it's supposed to: treats it as the value of a query string parameter and URL encodes it. #Ian is absolutely right that you need to split them up.
so I'm trying to send a simple String to my REST server from an Android app using androidannotations.
http://localhost:8080/TestServer_RESTJersey/api/lanceurs/parPays
Using Advanced REST client chrome extension, I send the parameter :
country=Europe
and it's working fine. Now my problem whith the Android app is that my request is received by the server, but the country parameter is always null. My others GET requests are all working perfectly.
Here is my RestClient class :
#Rest(converters = {MappingJacksonHttpMessageConverter.class, FormHttpMessageConverter.class})
public interface RestClient extends RestClientRootUrl, RestClientSupport{
#Get("/poke/simple")
public MessageResponse simplePoke();
#Get("/api/lanceurs/{name}")
public LaunchVehicleResponse nameRequest(String name);
//server doesn't get the parameter here...
#Post("/api/lanceurs/parPays")
public LaunchVehicleResponse countryRequest(String country);
}
Any help would be appreciated as usual, thanks!
EDIT :
server-side REST api :
#Path("api/lanceurs/parPays")
#POST
public String getLanceurByCountry(#FormParam("country") String country)
{
initData();
LaunchVehicleResponse lvr = new LaunchVehicleResponse();
ArrayList<LaunchVehicle> allv = myDatabase.getDataByCountry(country);
lvr.setData(allv);
return parseObjectToJson(lvr);
}
In JAX-RS, use #QueryParam annotation to inject URI query parameter into Java method. example,#QueryParam("country") String countryName,
Try the below, i guess, it should work
#Post("/api/lanceurs/parPays")
public LaunchVehicleResponse countryRequest(#QueryParam("country") String country);
Ok, it seems I figured out a way to get myself out of this mess.
I made a class LaunchVehicleRequest on my client, containing (among other things) a country String. When I need to send a request to my server, I instantiate this class and initialize LaunchVehicleRequest.country with the value I want (ex: "USA"). Then I send the whole object to my RestClient.
LaunchVehicleRequest lvreq = new LaunchVehicleRequest();
lvreq.setCountry("Europe");
LaunchVehicleResponse lvr = pm.countryRequest(lvreq);
...
#Rest(converters = {MappingJacksonHttpMessageConverter.class, FormHttpMessageConverter.class}, interceptors = { LoggingInterceptor.class } )
public interface RestClient extends RestClientRootUrl, RestClientSupport, RestClientHeaders{
#Post("/api/lanceurs/parPays")
public LaunchVehicleResponse countryRequest(LaunchVehicleRequest request);
}
I set up the same class on my server-side, which get the request as a string and then convert it in an object.
#Path("api/lanceurs/parPays")
#POST
public String getLanceurByCountry(String request)
{
// request={"country":"USA"}
//my json parsing function here
LaunchVehicleRequest lvreq = parseJsonToRequest(request);
...
}
I don't know is this is the best way, but hey it's working fine now and I'm using my LaunchVehicleRequest class for every different request I can need to, so it's not THAT bad I guess ^^'
Thanks everyone anyway ;)
As explained on the wiki, you can send form parameters this way:
#Rest(rootUrl = "http://company.com/ajax/services", converters = { FormHttpMessageConverter.class, MappingJackson2HttpMessageConverter.class })
public interface MyRestClient extends RestClientHeaders {
#RequiresHeader(HttpHeaders.CONTENT_TYPE)
#Post("/api/lanceurs/parPays")
public LaunchVehicleResponse countryRequest(MultiValueMap<String, Object> data);
}
MultiValueMap<String, Object> data = new LinkedMultiValueMap<>();
data.set("country, "Europe");
client.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE);
client.countryRequest(data);
I'm using Retrofit to access a RESTful api. The base url is:
http://api.example.com/service
This is the code for the interface:
public interface ExampleService {
#Headers("Accept: Application/JSON")
#POST("/album/featured-albums")
Call<List<Album>> listFeaturedAlbums();
}
and this is how I send request and receive the responce:
new AsyncTask<Void, Void, Response<List<Album>>>() {
#Override
protected Response<List<Album>> doInBackground(Void... params) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.example.com/service")
.addConverterFactory(GsonConverterFactory.create())
.build();
ExampleService service = retrofit.create(ExampleService.class);
try {
return service.listFeaturedAlbums().execute();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Response<List<Album>> listCall) {
Log.v("Example", listCall.raw().toString());
}
}.execute();
the log that I get is the weird thing:
V/Example﹕ Response{protocol=http/1.1, code=404, message=Not Found, url=http://api.example.com/album/featured-albums}
What's going on here?
Retrofit 2 uses the same rules that an <a href=""> would.
The leading / on your relative URL tells Retrofit that it is an absolute path on the host. Here's an example from a presentation I gave showing this:
Note the incorrect URL which was resolved at the bottom.
By removing the leading /, the URL then becomes relative and will combine with the path segments which are part of the base URL. Corrected in the presentation the final URL is now correct:
In your example you do not have a trailing / on the base URL. You probably want to add one so that relative paths are resolved on top of it rather than as a sibling of it.
I have the following controller set up:
#PreAuthorize("hasAuthority('ROLE_USER')")
#RequestMapping(value = "/me/avatar", method = RequestMethod.POST)
public #ResponseBody boolean setAvatar(Principal principal, MultipartHttpServletRequest request) {
String username = ((User) ((OAuth2Authentication) principal).getPrincipal()).getUsername();
MultipartFile file = request.getFile("avatar");
return Boolean.TRUE;
}
And when I use Square Retrofit to POST to this controller:
#Multipart
#POST("/user/me/avatar?access_token={access_token}")
void uploadAvatar(#Name("access_token") String accessToken, #Name("avatar") TypedFile image, retrofit.http.Callback<Boolean> callback);
I get a MultipartHttpServletRequest which has the "avatar" parameter, with the proper file name and everything, but no multipart files.
What am I doing wrong that would cause me to get MultipartParams but no MultipartFiles? I've tried various other TypedOutput formats, but I get the same result. If I hit the same controller from Postman (a Chrome plugin) everything works as expected, leading me to think it's a bug in Retrofit?
This was due to a bug in Retrofit, which has been fixed as of today. The above code now works to upload a file from Retrofit to a Spring based api server.