Volley failing multipart form data request - android

I am trying to send multipart data to a server, this data includes also some images.
So i extended Request in this way:
public class MultipartImageRequest extends Request<String> {
private MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
private static final String STRING_PART_NAME = "text";
private final Response.Listener<String> mListener;
private final ArrayList<Bitmap> mFiles = new ArrayList<>();
private HashMap<String, String> mParams = new HashMap<>();
public MultipartImageRequest(String url, Response.ErrorListener errorListener, Response.Listener<String> listener, ArrayList<Bitmap> files, HashMap<String, String> params)
{
super(Method.POST, url, errorListener);
mListener = listener;
mFiles.addAll(files);
mParams = params;
}
private void buildMultipartEntity()
{
int i = 0;
for(Bitmap image : mFiles) {
/*Compress bitmap*/
ByteArrayOutputStream bos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 50, bos);
entityBuilder.addPart("image_" + i, new ByteArrayBody(bos.toByteArray(), "image_" + i));
i++;
}
StringBuilder paramsBuilder = new StringBuilder();
Iterator<Map.Entry<String, String>> paramIterator = mParams.entrySet().iterator();
while (paramIterator.hasNext()) {
Map.Entry<String,String> entry = paramIterator.next();
entityBuilder.addPart(entry.getKey(), new StringBody(entry.getValue(), ContentType.DEFAULT_TEXT));
}
}
#Override
public String getBodyContentType()
{
return "multipart/form-data; charset=utf-8";
}
#Override
public byte[] getBody() throws AuthFailureError
{
buildMultipartEntity();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try
{
entityBuilder.build().writeTo(bos);
}
catch (IOException e)
{
VolleyLog.e("IOException writing to ByteArrayOutputStream");
}
return bos.toByteArray();
}
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response)
{
return Response.success("Uploaded", getCacheEntry());
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String,String> params = new HashMap<String, String>();
params.put("Content-Type","multipart/form-data; charset=utf-8");
return params;
}
#Override
protected void deliverResponse(String response)
{
mListener.onResponse(response);
}
}
The problem is that if i set as a content type application/json it works and sends data to the server, but obviously the server gives me back an error 400 because it is expecting a multipart request, rather if i set the content type as multipart/form-data; charset=utf-8 like in the code above Volley just doesn't send the request and gives me an error 500.
I am using RequestBin to check my data and it confirms what i said above.
Also i am using a Rest Client and the request is working with it and on iphone too.

I found the solution on my own, it is important to pass a boundary to the request, otherwise the server will not be able to take our parameters.
It can be done replacing this code:
public MultipartImageRequest(String url, Response.ErrorListener errorListener, Response.Listener<String> listener, ArrayList<Bitmap> files, HashMap<String, String> params)
{
super(Method.POST, url, errorListener);
mListener = listener;
mFiles.addAll(files);
mParams = params;
}
with this one:
public MultipartImageRequest(String url, Response.ErrorListener errorListener, Response.Listener<String> listener, ArrayList<Bitmap> files, HashMap<String, String> params)
{
super(Method.POST, url, errorListener);
mListener = listener;
mFiles.addAll(files);
mParams = params;
entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
entityBuilder.setBoundary(BOUNDARY);
}
and remember to set the boundary string in your Content-Type:
#Override
public String getBodyContentType()
{
return "multipart/form-data; boundary=" + BOUNDARY + "; charset=utf-8";
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String,String> params = new HashMap<String, String>();
params.put("Content-Type","multipart/form-data; boundary=" + BOUNDARY + "; charset=utf-8");
return params;
}

Related

Android Volley getParams Not Called for Custom Request

I have created a custom Volley Request class which extends Request<NetworkResponse>. Here is the code for that custom class:
public class MultipartRequest extends Request<NetworkResponse> {
private final Response.Listener<NetworkResponse> mListener;
private final Response.ErrorListener mErrorListener;
private final Map<String, String> mHeaders;
private final Map<String, String> mParams;
private final String mMimeType;
private final byte[] mMultipartBody;
public MultipartRequest(String url, Map<String, String> headers, Map<String, String> params, String mimeType, byte[] multipartBody, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) {
super(Method.POST, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
this.mHeaders = headers;
this.mParams = params;
this.mMimeType = mimeType;
this.mMultipartBody = multipartBody;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Log.i("MultipartRequest", "headers = " + mHeaders);
return mHeaders;
}
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Log.i("MultipartRequest", "params = " + mParams);
return mParams;
}
#Override
public String getBodyContentType() {
return mMimeType;
}
#Override
public byte[] getBody() throws AuthFailureError {
return mMultipartBody;
}
#Override
protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) {
try {
return Response.success(
response,
HttpHeaderParser.parseCacheHeaders(response));
} catch (Exception e) {
return Response.error(new ParseError(e));
}
}
#Override
protected void deliverResponse(NetworkResponse response) {
mListener.onResponse(response);
}
#Override
public void deliverError(VolleyError error) {
mErrorListener.onErrorResponse(error);
}
}
I have inserted the Log.i statements within getHeaders() and getParams() to ensure they are called properly.I create the request as follows:
final Map<String, String> volleyParams = new HashMap<>();
volleyParams.put("size", String.valueOf(data.imageSize));
volleyParams.put("width", String.valueOf(data.imageWidth));
volleyParams.put("height", String.valueOf(data.imageHeight));
volleyParams.put("isIndex", updateProfilePhoto ? "1" : "0");
final Context context = this;
final String twoHyphens = "--";
final String lineEnd = "\r\n";
final String boundary = "apiclient-" + System.currentTimeMillis();
final String mimeType = "multipart/form-data;boundary=" + boundary;
byte[] multipartBody = new byte[0];
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
try {
// the first file
buildPart(dos, data.thumbnailImageArray, "file0");
// the second file
buildPart(dos, data.scaledImageArray, "file1");
// send multipart form data necesssary after file data
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
// pass to multipart body
multipartBody = bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
Map<String, String> headers = new HashMap<String, String>();
headers.put("User-Agent", Authentication.getUserAgent());
headers.put("X-XX-API", Authentication.getKey());
MultipartRequest multipartRequest = new MultipartRequest(BASE_URL + "member/photos", headers, volleyParams, mimeType, multipartBody, new Response.Listener<NetworkResponse>() {
#Override
public void onResponse(NetworkResponse response) {
Log.i(LOG_TAG, "response: " + response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.i(LOG_TAG, "statusCode = " + error.networkResponse.statusCode);
Log.i(LOG_TAG, "data = " + error.networkResponse.data);
Log.i(LOG_TAG, "headers = " + error.networkResponse.headers);
Log.i(LOG_TAG, "notModified = " + error.networkResponse.notModified);
Log.i(LOG_TAG, "networkTimeMs = " + error.networkResponse.networkTimeMs);
}
});
MySingleton.getInstance(context).addToRequestQueue(multipartRequest);
The call is performed, however the getParams() is never called. How can I ensure the getParams() is always called?
I have had the same problem. You need to override the getBody method and convert all your params to an array of bytes. For example, for a JSON request.
#Override
public String getBodyContentType() {
return "application/json; charset=utf-8";
}
#Override
public byte[] getBody() {
if (mParams == null) {
return null;
}
try {
return new Gson().toJson(mParams).getBytes("utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}

Pass header and Params in same gson request using volley

I am trying to make PUT request using gson + volley where i have to send Header and Params both in the same request. I am getting a success response but data is not being sent to server. I am using the following code
public GsonRequest(int method,
String url,
Class<T> clazz,
Map<String, String> headers,
Map<String, String> params,
Listener<T> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
this.clazz = clazz;
this.params = params;
this.listener = listener;
this.headers = headers;
mGson = new Gson();
}
#Override
protected Map<String, String> getParams() throws AuthFailureError {
return params != null ? params : super.getParams();
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers != null ? headers : super.getHeaders();
}
In Activity i am using above gson
private void updateProfile(String fname, String lname, String email, String mob) {
String tag_json_obj = "json_obj_req";
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Authorization", "JWT " + tinyDB.getString(Constants.MY_SHARED_PREF_TOKEN));
Log.d("Authorization---", "JWT " + tinyDB.getString(Constants.MY_SHARED_PREF_TOKEN));
String url = Constants.PATCH_USER+tinyDB.getString(Constants.MY_USER_ID);
Map<String, String> params = new HashMap<String, String>();
params.put("Content-type", "application/x-www-form-urlencoded");
params.put("first_name",fname);
params.put("last_name",lname);
params.put("email",email);
params.put("mobile_no", mob);
// url = url+"?first_name="+fname+"&last_name="+lname+"&email="+email+"&mob_no="+mob;
Log.d("URL -- ", "" + url);
GsonRequest<AddtoWishlistResponse> myReq = new GsonRequest<AddtoWishlistResponse>(
Request.Method.PATCH,
url,
AddtoWishlistResponse.class,
headers,
params,
createMyReqSuccessListener(),
createMyReqErrorListener());
AppController.getInstance().addToRequestQueue(myReq, tag_json_obj);
}
You can refer to the following sample code:
...
public GsonRequest(int method, String url, Class<T> clazz, Map<String, String> headers, String requestBody,
Response.Listener<T> listener, Response.ErrorListener errorListener) {
super(method, url, errorListener);
this.mClass = clazz;
this.mHeaders = headers;
this.mRequestBody = requestBody;
this.mListener = listener;
this.mErrorListener = errorListener;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return (mHeaders != null) ? mHeaders : super.getHeaders();
}
#Override
public String getBodyContentType() {
return "application/json; charset=utf-8";
}
#Override
public byte[] getBody() throws AuthFailureError {
try {
return mRequestBody == null ? null : mRequestBody.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
mRequestBody, "utf-8");
return null;
}
}
Other methods such as parseNetworkResponse(), deliverResponse()... you should refer this Google training documentation - Implementing a Custom Request
For request body, you can refer the following:
Map<String, String> stringMap = new HashMap<>();
stringMap.put("key1", "value1");
stringMap.put("key2", "value2");
final String mRequestBody = buildRequestBody(stringMap);
...
private String buildRequestBody(Object content) {
String output = null;
if ((content instanceof String) ||
(content instanceof JSONObject) ||
(content instanceof JSONArray)) {
output = content.toString();
} else if (content instanceof Map) {
Uri.Builder builder = new Uri.Builder();
HashMap hashMap = (HashMap) content;
if (hashMap != null) {
Iterator entries = hashMap.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
builder.appendQueryParameter(entry.getKey().toString(), entry.getValue().toString());
entries.remove();
}
output = builder.build().getEncodedQuery();
}
}
return output;
}
Hope this helps!

Uploading image using Google Volley library

I am trying to upload an image using Volley library in android but no data is being send to the server side as the file is created but it does not contain any data.
public class ProfilePicSendReq extends Request<String>
{
private MultipartEntityBuilder mBuilder = MultipartEntityBuilder.create();
private final Response.Listener<String> mListener;
private final File mImageFile;
protected Map<String, String> headers;
Context context;
public ProfilePicSendReq(Context cntxt, String url, Listener<String> listener, ErrorListener errorListener, File imageFile)
{
super(Method.POST, url, errorListener);
mListener = listener;
mImageFile = imageFile;
buildMultipartEntity();
context = cntxt;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError
{
headers = super.getHeaders();
if (headers == null
|| headers.equals(Collections.emptyMap()))
{
headers = new HashMap<String, String>();
}
headers.put("Accept", "text/plain");
return headers;
}
private void buildMultipartEntity()
{
mBuilder.addBinaryBody(mImageFile.getName(), mImageFile, ContentType.create("image/jpeg"), "profilePic");
mBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
mBuilder.setLaxMode().setBoundary("xx").setCharset(Charset.forName("UTF-8"));
}
#Override
public String getBodyContentType()
{
String contentTypeHeader = mBuilder.build().getContentType().getValue();
return contentTypeHeader;
}
#Override
public byte[] getBody() throws AuthFailureError
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
mBuilder.build().writeTo(bos);
} catch (IOException e) {
Log.d("PicUploadMsg : ", "IOException writing to ByteArrayOutputStream bos, building the multipart request.");
}
return bos.toByteArray();
}
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response)
{
String result = new String(response.data);
return Response.success(result, HttpHeaderParser.parseCacheHeaders(response));
}
#Override
protected void deliverResponse(String response)
{
mListener.onResponse(response);
}
}
It is the famous sending Mulipart data using volley i have tried several time but no data appears to leave from the client side.

how to perform file upload using Volley in android?

I want to upload files(images,documents etc) from my android application to my server.I'm using Volley library for network calls in my application.
I'm using the following code to upload files but it is not performing any operation.(showing volley timeout error finally)
public class MultipartRequest extends Request<String> {
private MultipartEntity entity = new MultipartEntity();
private static final String FILE_PART_NAME = "file";
private final Response.Listener<String> mListener;
private final File mFilePart;
private String mStringPart,accessToken;
public MultipartRequest(String url, Response.ErrorListener errorListener, Response.Listener<String> listener, File file,String accessToken)
{
super(Method.POST, url, errorListener);
mListener = listener;
mFilePart = file;
this.accessToken = accessToken;
buildMultipartEntity();
}
private void buildMultipartEntity()
{
entity.addPart(FILE_PART_NAME, new FileBody(mFilePart));
try
{
entity.addPart("Content-Disposition", new StringBody("form-data"));
entity.addPart("dir_path", new StringBody("IzEzOjE3"));
}
catch (UnsupportedEncodingException e)
{
VolleyLog.e("UnsupportedEncodingException");
}
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = super.getHeaders();
if (headers == null
|| headers.equals(Collections.emptyMap())) {
headers = new HashMap<String, String>();
}
headers.put("Content-Type", "multipart/form-data");
headers.put("_pkta",accessToken);
return headers;
}
#Override
public String getBodyContentType()
{
return entity.getContentType().getValue();
}
#Override
public byte[] getBody() throws AuthFailureError
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try
{
entity.writeTo(bos);
}
catch (IOException e)
{
VolleyLog.e("IOException writing to ByteArrayOutputStream");
}
return bos.toByteArray();
}
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response)
{
return Response.success("Uploaded", getCacheEntry());
}
#Override
protected void deliverResponse(String response)
{
mListener.onResponse(response);
}
}
please help me on how to implement file upload in android(either by volley or any other)
Try
public MultiPartRequest(String url, String filePath, Response.Listener<String> listener, Response.ErrorListener errorListener)
{
super(Method.POST, url, errorListener);
entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
file = new File(filePath);
mListener = listener;
buildMultipartEntity();
}
also, in my case I didn't override getHeaders, just passed other values with addPart, e.g:
entity.addPart("file", new FileBody(file, "image/jpeg")); // for image
entity.addPart("id", new StringBody(userid));
hope this helps.

Error trying send an image file with Volley?

I have an image that I'm trying send to my web service with others params using Volley library. The problem is I don't know how can I pass this image about the url using POST.
Looking for a solution I have founded any suggestions to use MultiPart and I'm trying implement that but still can't do this works.
I created other constructor in my Application, this constructor should receive a File but doesn't work also and HashMap doesn't accept File param
How can I do this ?
I'm trying this.
public class ApplicationController extends Request<JSONObject>{
private Map<String, String> headers;
private Map<String, String> params;
private Response.Listener<JSONObject> listener;
private File imageFile;
private MultipartEntityBuilder mBuilder = MultipartEntityBuilder.create();
public ApplicationController(String url, Map<String, String> params, Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) {
super(Method.GET, url, errorListener);
this.listener = listener;
this.params = params;
}
public ApplicationController(int method, String url, Map<String, String> params, Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) {
super(method, url, errorListener);
this.listener = listener;
this.params = params;
}
/** construtor to send image */
public ApplicationController(int method,
String url,
Map<String, String> params,
Response.Listener<JSONObject> listener,
Response.ErrorListener errorListener,
File file) {
super(method, url, errorListener);
this.listener = listener;
this.params = params;
this.imageFile = file;
}
protected Map<String, String> getParams() throws AuthFailureError {
return params;
};
public Map<String, String> getHeaders() throws AuthFailureError {
headers = new HashMap<String, String>();
String cred = String.format("%s:%s", BasicAuthenticationRest.USERNAME, BasicAuthenticationRest.PASSWORD);
String auth = "Basic " + Base64.encodeToString(cred.getBytes(), Base64.DEFAULT);
headers.put("Authorization", auth);
return headers;
};
private void buildMultipartEntity(){
mBuilder.addBinaryBody("", imageFile, ContentType.create("image/png"), imageFile.getName());
mBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
mBuilder.setLaxMode().setBoundary("xx").setCharset(Charset.forName("UTF-8"));
}
#Override
public String getBodyContentType(){
String contentTypeHeader = mBuilder.build().getContentType().getValue();
return contentTypeHeader;
}
#Override
public byte[] getBody() throws AuthFailureError{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try{
mBuilder.build().writeTo(bos);
}catch (IOException e){
VolleyLog.e("IOException writing to ByteArrayOutputStream bos, building the multipart request.");
}
return bos.toByteArray();
}
#Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return Response.success(new JSONObject(jsonString), HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
#Override
protected void deliverResponse(JSONObject response) {
listener.onResponse(response);
}
}
Using
/** add an user and upload your foto(image) */
public ApplicationController insert(Usuario u, File imageFile, final UsuarioAdapter listener){
boolean insert = false;
HashMap<String, String> params = new HashMap<String, String>();
params.put("nome", u.getNome());
params.put("email", u.getEmail());
params.put("senha", u.getSenha());
params.put("tipo", "usuarios");
params.put("acao", "add");
params.put("device_tipo", "android");
params.put("device", AndroidReturnId.getAndroidId());
params.put("uploadedfile", imageFile);
ApplicationController apc = new ApplicationController(Method.POST, urlPost.toString(), params,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject obj) {
try {
if(obj.getString("cod").equals("999")){
listener.usuarioIsAdded(true);
}else{
listener.usuarioIsAdded(false);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError arg0) {
Log.e("ERROR METHOD:", "insert in UsuarioDAO: " + arg0.getLocalizedMessage());
}
}, File file);
return apc;
}
Encode the file to a base 64 encoded string and set that in the body (just make sure your server accepts this format!). Google how to do this in java, it's everywhere
params.put("uploadedfile", base64EncodedImageFile);

Categories

Resources