Java Volley Request Multipart Form with Image Data - java

I am trying to upload data to my web app using a REST API. I need to send an image with other parameters as multipart data. I have the method below based on some posts that I have viewed on here. When the request is submitted it fails and from the server logs it looks like it is not reading the parameters and failing with the ID.
I am confused as how to structure the request using Volley. If I send the equivalent using Insomnia as shown below then it is successful. I have replicated using the same bearer token.
private void uploadPicture() {
progressDialog = new ProgressDialog(CameraActivity.this);
progressDialog.setMessage("Uploading, please wait...");
progressDialog.show();
Log.d("URL", URL + PICNS);
//converting image to base64 string
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Bitmap compressedImg = getResizedBitmap(imageBitmap,600);
compressedImg.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] imageBytes = baos.toByteArray();
final String imageString = Base64.encodeToString(imageBytes, Base64.DEFAULT);
Log.d("TOKEN", token);
//sending image to server
JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST,URL + PICNS,null, new Response.Listener<JSONObject>(){
#Override
public void onResponse(JSONObject response) {
progressDialog.dismiss();
Log.d("RESPONSE", response.toString());
}
},new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError volleyError) {
Log.d("Error", volleyError.toString());
Toast.makeText(CameraActivity.this, "Some error occurred -> "+volleyError, Toast.LENGTH_LONG).show();;
}
}) {
//adding parameters to send
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> parameters = new HashMap<String, String>();
parameters.put("route_id","1");
parameters.put("lat","52.395728");
parameters.put("lng","-1.992031");
parameters.put("description", " ");
parameters.put("caption", "Wow Uploaded This");
parameters.put("picture", imageString);
return parameters;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + token);
return headers;
}
};
RequestQueue rQueue = Volley.newRequestQueue(CameraActivity.this);
rQueue.add(request);
}

I used the library from here to get send the multipart request and it seems to be working for me.

Related

Volley request with headers and body params

I need to make an api request which.
Two headers :
Accept
Authorization
Five body params.
Number
Make
Model
Description
Plates
Through postman everything works great.
But when i try through android app i can't get through.
Note: Login through the same host works great so the setup its not the problem i think my main problem is in api call.
public void add(View view) {
RequestQueue queue = Volley.newRequestQueue(this);
String URL = "http://10.0.2.2:8000/api/trucks";
StringRequest request = new StringRequest(Request.Method.POST, URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jsonObj = new JSONObject(response);
// parse response
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
NetworkResponse response = error.networkResponse;
String errorMsg = "";
if (response != null && response.data != null) {
String errorString = new String(response.data);
}
}
}
) {
#Override
public Map<String, String> getHeaders() {
HashMap<String, String> headers = new HashMap<>();
headers.put("Accept", "application/json");
headers.put("Authorization", "Bearer " + myToken);
return headers;
}
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
TextInputEditText number = findViewById(R.id.textInputEditTextNumber);
TextInputEditText make = findViewById(R.id.textInputEditTextMake);
TextInputEditText model = findViewById(R.id.textInputEditTextModel);
TextInputEditText description = findViewById(R.id.textInputEditTextDescription);
TextInputEditText plates = findViewById(R.id.textInputEditTextPlates);
params.put("number", number.getText().toString());
params.put("make", make.getText().toString());
params.put("model", model.getText().toString());
params.put("description", description.getText().toString());
params.put("plates", plates.getText().toString());
return params;
}
};
queue.add(request);
}
Edit: by Solution #1.
public void add(View view) throws JSONException {
RequestQueue queue = Volley.newRequestQueue(this);
TextInputEditText number = findViewById(R.id.textInputEditTextNumber);
TextInputEditText make = findViewById(R.id.textInputEditTextMake);
TextInputEditText model = findViewById(R.id.textInputEditTextModel);
TextInputEditText description = findViewById(R.id.textInputEditTextDescription);
TextInputEditText plates = findViewById(R.id.textInputEditTextPlates);
JSONObject jsonObject = new JSONObject();
jsonObject.put("Number", number.getText().toString());
jsonObject.put("Make", make.getText().toString());
jsonObject.put("Model", model.getText().toString());
jsonObject.put("Description", description.getText().toString());
jsonObject.put("Plates", plates.getText().toString());
final String requestBody = jsonObject.toString();
JsonObjectRequest jsonRequest = new JsonObjectRequest(Request.Method.POST, "http://10.0.2.2:8000/api/trucks", jsonObject,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
//now handle the response
Toast.makeText(truck_add.this, response.toString(), Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
//handle the error
Toast.makeText(truck_add.this, "An error occurred", Toast.LENGTH_SHORT).show();
error.printStackTrace();
}
}) { //this is the part, that adds the header to the request
#Override
public Map<String, String> getHeaders() {
Map<String, String> params = new HashMap<String, String>();
params.put("Accept", "application/json");
params.put("Authorization", myToken);
return params;
}
};
queue.add(jsonRequest);
}
Postman :
When you want to pass the data through the body, you need to create a json object before string request.
try this way,
1. create a string url for request.
2. create json object for body data and pass data to it. Like,
JSONObject jsonObject= new JSONObject();
jsonObject.put("Number", address.getNumber());
jsonObject.put("Make", address.getMake());
jsonObject.put("Model", address.getModel());
jsonObject.put("Description", address.getDescription());
jsonObject.put("Plates", address.getPlates());
final String requestBody=jsonObject.toString();
3. After this, apply stringRequest.
4. Now, add below lines before header method and after error method.
#Override
public String getBodyContentType() {
return "application/json; charset=utf-8";
}
#Override
public byte[] getBody() throws AuthFailureError {
try {
return requestBody == null ? null : requestBody.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8");
return null;
}
5. In your getHeaders() method, use Content-Type for "application/json" and for Authorization you must use only token without (Bearer).
Done.
public void add(View view) throws JSONException {
String URL = "http://10.0.2.2:8000/api/trucks";
//removed views initialization from here.
//you need to initialize views in oncreate() / oncreateView() method.
/*first create json object in here.
then set keys as per required body format with values
*/
JSONObject jsonObject = new JSONObject();
jsonObject.put("Number", number.getText().toString());
jsonObject.put("Make", make.getText().toString());
jsonObject.put("Model", model.getText().toString());
jsonObject.put("Description", description.getText().toString());
jsonObject.put("Plates", plates.getText().toString());
final String requestBody = jsonObject.toString();
RequestQueue queue = Volley.newRequestQueue(this);
StringRequest request = new StringRequest(Request.Method.POST, URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jsonObj = new JSONObject(response);
// parse response
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
NetworkResponse response = error.networkResponse;
String errorMsg = "";
if (response != null && response.data != null) {
String errorString = new String(response.data);
}
}
}
) { //this is the part, that adds the header to the request
//this is where you need to add the body related methods
#Override
public String getBodyContentType() {
return "application/json; charset=utf-8";
}
#Override
public byte[] getBody() throws AuthFailureError {
try {
return requestBody == null ? null : requestBody.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s
using %s", requestBody, "utf-8");
return null;
}
#Override
public Map<String, String> getHeaders() {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json");
params.put("Authorization", myToken);
return params;
}
};
queue.add(jsonRequest);
}
Put Log in every method to check which method being executed and for possible error
If there is an error after this, then post error from logcat and we will solve it quickly.

How to send base64 string to server using volley

I'm using volley library in android to call our server when try to send base64 encoded image to server the request fail but when remove the encoded string the request success without any issue.
I use the following code to encode my image
public static String imgToBase64(Bitmap bitmap) {
ByteArrayOutputStream out = null;
try {
out = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);
out.flush();
out.close();
byte[] imgBytes = out.toByteArray();
return Base64.encodeToString(imgBytes, Base64.DEFAULT);
} catch (Exception e) {
return null;
} finally {
try {
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
and send request using volley with the following
public void sendDataToServer(final ApiResponse.Listener<JSONObject> listener, final ApiResponse.ErrorListener errorListener, String image) {
String url = String.format(ASK, image);
url = URLEncoder.encode(url);
JsonRequest sendRequest = new JsonRequest(Request.Method.POST, url,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
listener.onResponse(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
if (error != null) {
if (error.networkResponse != null) {
errorListener.onErrorResponse(error.networkResponse.statusCode, null);
} else {
errorListener.onErrorResponse(500, null);
}
}
}
});
RequestPipeline.getInstance(mContext).addToRequestQueue(sendRequest);
}
Looking into your code it looks like you try to pass whole encoded image as a part of URL. Obviously this fail because servers typically don't support URL longer than a few thousand of symbols. To pass long data to the server you should use body of the POST. I don't know what your JsonEmptyResponseRequest class is and what format the server expects (how ASK is specified). Just to give you an idea how this might be implemented
StringRequest sendRequest = new StringRequest(Request.Method.POST, url,
new Response.Listener ...,
new Response.ErrorListener...){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put(IMAGE_PARAM_NAME, image);
return params;
}
};
In real life you probably want to create your own sub-class of Request<T> and pass image there as a constructor parameter and hide exactly details of what server expects inside that class. Note that JsonRequest overrides getBody and getBodyContentType to use JSON so this will not work with JsonRequest subclass.

Android Google CloudPrint Submit a PDF print job via URL API

Im having trouble getting the correct code to send submit a multipart form with a PDF included to google cloud print( the reason I'm not using the inbuilt intent in android is that you cant automatically select a printer it need to be done manually when using the intent)
I have been using Volley to submit it but I have been reading its not great with large files?
final RequestQueue queue = Volley1.getInstance(this).getRequestQueue();
String url3 = "https://www.google.com/cloudprint/submit";
final VolleyMultipartRequest multipartRequest = new VolleyMultipartRequest(Request.Method.POST, url3 ,new Response.Listener<NetworkResponse>() {
#Override
public void onResponse(NetworkResponse response) {
String resultResponse = new String(response.data);
Log.d("responseis",resultResponse);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
}){
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
// the POST parameters:
params.put("redirect_uri", "xxxxx");
params.put("printerid","xxxxxx");
params.put("title", "result1.pdf");
params.put("contentType", "application/pdf");
params.put("ticket", "{\"print\":{},\"version\":\"1.0\"}");
params.put("hl", "en");
return params;
}
#Override
public Map getHeaders() throws AuthFailureError {
Map headers = new HashMap();
headers.put("Authorization", "OAuth"+" "+res);
headers.put("Content-Type", "multipart/form-data; boundary=__1466595844361__");
//headers.put("Content-Type","application/x-www-form-urlencoded");
//Logger.debugE(Constants.accesstoken, headers.toString());
return headers;
}
#Override
protected Map<String, DataPart> getByteData() {
Map<String, DataPart> params = new HashMap<>();
//String yourFilePath = Environment.getExternalStorageDirectory().getAbsolutePath()+ "/PDFs/result1.pdf";
String yourFilePath=Environment.getExternalStorageDirectory().getAbsolutePath()+ "/PDFs/result1.pdf";
File dir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS), "PDF_reader");
File text = new File(dir + File.separator + "test_r.pdf");
String input = text.getAbsolutePath();
byte[] data = new byte[(int) input.length()];
try {
//convert file into array of bytes
data = FileUtils.readFileToByteArray(text);
}catch(Exception e){
e.printStackTrace();
}
params.put("content", new DataPart("result1.pdf",data , "application/pdf"));
return params;
}
};
queue.add(multipartrequest);
After a lot of trial and error I ended up using OKHTTP library and I was successfully able to submit a print job to google cloud print using the API. It was a bit tricky to get the multipart form with the pdf file included but the correct working request is below. Obviously the token and printerid are retrieved in other requests and need to be added manually.
public void run() throws Exception {
//File directory
File dir = Environment.getExternalStorageDirectory();
File file = new File(dir, "PDFs/result1.pdf");
if(file.canRead()){
Log.d("file", "can read");
}else {Log.d("file","cant read");};
// Final request body
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("content","result1.pdf",RequestBody.create(MediaType.parse("application/pdf"), file))
.build();
//URL Parameters
HttpUrl url = new HttpUrl.Builder()
.scheme("https")
.host("www.google.com")
.addPathSegments("cloudprint/submit")
.addQueryParameter("printerid", "82d70sde-694b-3385-f263-164cb04320c3")
.build();
Request request = new Request.Builder()
.header("Authorization", "OAuth ya29.Cl0jAxDuQni_Dskz6Y2r-wRaJVMxEw8fy5hPNfAm02pDLnZc9HX-RfHpmMoS0OL-Wv_SKxBtYIwRK9jVpJDwl7Qs-RFt02Qc5Yoid4w1kV8b4vBIpcleIQ8lBEto")
.url(url)
.post(requestBody)
.build();
client.newCall(request)
.enqueue(new Callback() {
#Override
public void onFailure(final Call call, IOException e) {
// Error
runOnUiThread(new Runnable() {
#Override
public void run() {
// For the example, you can show an error dialog or a toast
// on the main UI thread
}
});
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
String res = response.body().string();
// Do something with the response
}
});
}

How to post json with volley to php web service

I am trying to Post Json to my restful API with volley but it doesn't work. So far i have tested my web service by sending a json payload through the Advance rest client app on chrome and it returns a json response.... but when i try it with Volley it returns onErrorResponse. Please can someone tell me how to solve this problem, thanks in advance.
Json payLoad:
{"country":"isreal","mobile":"009988778"}
Code
private void processLogin() {
showProgressDialog();
JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.POST,
Const.LOGIN_URL, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.i("JSON response", "JSON Posting" + response.toString());
hideProgessDialog();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
VolleyLog.d(ERROR_TAG, "Error: " + volleyError.getMessage());
hideProgessDialog();
}
}){
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json");
return headers;
}
#Override
protected Map<String, String> getParams(){
String country_value = country.getSelectedItem().toString();
String mobile_value = mobile.getText().toString();
Map<String, String> params = new HashMap<>();
params.put("country",country_value);
params.put("mobile", mobile_value);
return params;
}
};
AppController.getInstance().addToRequestQueue(jsonObjReq, login_tag);
}
What you are doing is, you are trying to send a JSON as a part of Headers which won't work.
This is what you need -
JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.POST,
Const.LOGIN_URL, **->YOUJSONOBJECTHERE<-**, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.i("JSON response", "JSON Posting" + response.toString());
hideProgessDialog();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
VolleyLog.d(ERROR_TAG, "Error: " + volleyError.getMessage());
hideProgessDialog();
}
});
You need to send JSONObject as a part of request not as a part of request headers. Try it out and let me, if it fixes the issue else we troubleshoot it.
And as you already using JSONObjectRequest, you niether need to set the content type nor override getParams() until or unless you need to send some extra information in your header like AgentType, tokens etc.
Convert object to json string.
Override getBody of Volley request
object
Override getBodyContentType to set as json
Gson gson = new Gson();
final String json = gson.toJson(loginDTO);
GsonRequest<EmailLoginRestResponse> jsObjRequest = new GsonRequest<EmailLoginRestResponse>(
Request.Method.POST, url,
EmailLoginRestResponse.class, null,
this.createLoginRequestSuccessListener(),
this.createLoginErrorListener()){
#Override
public byte[] getBody() throws AuthFailureError {
return json.getBytes();
}
#Override
public String getBodyContentType() {
return "application/json; charset=" + this.getParamsEncoding();
}
};
jsObjRequest.setShouldCache(false);
this.mRequestQueue.add(jsObjRequest);
GsonRequest.java
https://github.com/ogrebgr/android_volley_examples/blob/master/src/com/github/volley_examples/toolbox/GsonRequest.java

How to do a Volley PUT to a drupal table

I'm attempting to do a PUT in x-www-form-urlencoded to a drupal table with Volley from my Android app. I'm try to send a 'type' and 'value', in the params and I get a 500 error. A basic StringRequest returns 404.
Here's my latest code. I've only found one or two entries that touch on the Volley Put. Any help would be appreciated. Have a great day.
private void postTestAnswerResult(String id, String answerResult) {
StringRequest req = null;
requestQueue = Volley.newRequestQueue(this);
final String baseURL = "http://blah.blah.com/api/answer/";
String URL = baseURL + id;
// Post params to be sent to the server
HashMap<String, String> params = new HashMap<String, String>();
params.put("Content-Type","application/x-www-form-urlencoded");
params.put("type", answerResult);
req = new StringRequest(Request.Method.PUT, URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
VolleyLog.v("Response:%n %s", response.toString());
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
try {
String responseBody = new String(
volleyError.networkResponse.data, "utf-8");
JSONObject jsonObject = new JSONObject(responseBody);
} catch (JSONException e) {
// Handle a malformed json response
} catch (UnsupportedEncodingException error) {
}
}
}
);
requestQueue.add(req);
}
In case you are still having problems with this, as #Meier points out in a comment above, you are not using the params variable, or rather you aren't using it correctly. The data doesn't get sent to the server, and the server is probably expecting the data resulting in the 500 error.
You need to override the getParams method of the StringRequest call in order to send the data. So, the following would be closer to getting the job done:
private void postTestAnswerResult(String id, String answerResult) {
StringRequest req = null;
requestQueue = Volley.newRequestQueue(this);
final String baseURL = "http://blah.blah.com/api/answer/";
String URL = baseURL + id;
req = new StringRequest(Request.Method.PUT, URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
VolleyLog.v("Response:%n %s", response.toString());
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
try {
String responseBody = new String(
volleyError.networkResponse.data, "utf-8");
JSONObject jsonObject = new JSONObject(responseBody);
} catch (JSONException e) {
// Handle a malformed json response
} catch (UnsupportedEncodingException error) {
}
}
}
) {
#Override
protected Map<String, String> getParams()
{
HashMap<String, String> params = new HashMap<String, String>();
// params.put("Content-Type","application/x-www-form-urlencoded"); This shouldn't be here. This is a HTTP header. If you want to specify header you should also override getHeaders.
params.put("type", answerResult);
return params;
}
};
requestQueue.add(req);
Browser don't ignore 500 errors. They very often show up as ugly messages in the browser window.

Categories

Resources