Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Upload file with Multipart Request Volley Android
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import java.io.ByteArrayOutputStream;
/**
* Sketch Project Studio
* Created by Angga on 12/04/2016 14.27.
*/
public class AppHelper {
/**
* Turn drawable resource into byte array.
*
* @param context parent context
* @param id drawable resource id
* @return byte array
*/
public static byte[] getFileDataFromDrawable(Context context, int id) {
Drawable drawable = ContextCompat.getDrawable(context, id);
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 0, byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
}
/**
* Turn drawable into byte array.
*
* @param drawable data
* @return byte array
*/
public static byte[] getFileDataFromDrawable(Context context, Drawable drawable) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
}
}
/**
* Sketch Project Studio
* Created by Angga 20/04/2016 19:32
*/
public class MainActivity extends AppCompatActivity {
private EditText mNameInput;
private EditText mLocationInput;
private EditText mAboutInput;
private EditText mContact;
private ImageView mAvatarImage;
private ImageView mCoverImage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNameInput = (EditText) findViewById(R.id.input_name);
mLocationInput = (EditText) findViewById(R.id.input_location);
mAboutInput = (EditText) findViewById(R.id.input_about);
mContact = (EditText) findViewById(R.id.input_contact);
mAvatarImage = (ImageView) findViewById(R.id.avatar);
mCoverImage = (ImageView) findViewById(R.id.cover);
// do anything before post data.. or triggered after button clicked
saveProfileAccount();
}
private void saveProfileAccount() {
// loading or check internet connection or something...
// ... then
String url = "http://www.angga-ari.com/api/something/awesome";
VolleyMultipartRequest multipartRequest = new VolleyMultipartRequest(Request.Method.POST, url, new Response.Listener<NetworkResponse>() {
@Override
public void onResponse(NetworkResponse response) {
String resultResponse = new String(response.data);
try {
JSONObject result = new JSONObject(resultResponse);
String status = result.getString("status");
String message = result.getString("message");
if (status.equals(Constant.REQUEST_SUCCESS)) {
// tell everybody you have succed upload image and post strings
Log.i("Messsage", message);
} else {
Log.i("Unexpected", message);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
NetworkResponse networkResponse = error.networkResponse;
String errorMessage = "Unknown error";
if (networkResponse == null) {
if (error.getClass().equals(TimeoutError.class)) {
errorMessage = "Request timeout";
} else if (error.getClass().equals(NoConnectionError.class)) {
errorMessage = "Failed to connect server";
}
} else {
String result = new String(networkResponse.data);
try {
JSONObject response = new JSONObject(result);
String status = response.getString("status");
String message = response.getString("message");
Log.e("Error Status", status);
Log.e("Error Message", message);
if (networkResponse.statusCode == 404) {
errorMessage = "Resource not found";
} else if (networkResponse.statusCode == 401) {
errorMessage = message+" Please login again";
} else if (networkResponse.statusCode == 400) {
errorMessage = message+ " Check your inputs";
} else if (networkResponse.statusCode == 500) {
errorMessage = message+" Something is getting wrong";
}
} catch (JSONException e) {
e.printStackTrace();
}
}
Log.i("Error", errorMessage);
error.printStackTrace();
}
}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("api_token", "gh659gjhvdyudo973823tt9gvjf7i6ric75r76");
params.put("name", mNameInput.getText().toString());
params.put("location", mLocationInput.getText().toString());
params.put("about", mAvatarInput.getText().toString());
params.put("contact", mContactInput.getText().toString());
return params;
}
@Override
protected Map<String, DataPart> getByteData() {
Map<String, DataPart> params = new HashMap<>();
// file name could found file base or direct access from real path
// for now just get bitmap data from ImageView
params.put("avatar", new DataPart("file_avatar.jpg", AppHelper.getFileDataFromDrawable(getBaseContext(), mAvatarImage.getDrawable()), "image/jpeg"));
params.put("cover", new DataPart("file_cover.jpg", AppHelper.getFileDataFromDrawable(getBaseContext(), mCoverImage.getDrawable()), "image/jpeg"));
return params;
}
};
VolleySingleton.getInstance(getBaseContext()).addToRequestQueue(multipartRequest);
}
}
package com.sketchproject.infogue.modules;
import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* Custom request to make multipart header and upload file.
*
* Sketch Project Studio
* Created by Angga on 27/04/2016 12.05.
*/
public class VolleyMultipartRequest extends Request<NetworkResponse> {
private final String twoHyphens = "--";
private final String lineEnd = "\r\n";
private final String boundary = "apiclient-" + System.currentTimeMillis();
private Response.Listener<NetworkResponse> mListener;
private Response.ErrorListener mErrorListener;
private Map<String, String> mHeaders;
/**
* Default constructor with predefined header and post method.
*
* @param url request destination
* @param headers predefined custom header
* @param listener on success achieved 200 code from request
* @param errorListener on error http or library timeout
*/
public VolleyMultipartRequest(String url, Map<String, String> headers,
Response.Listener<NetworkResponse> listener,
Response.ErrorListener errorListener) {
super(Method.POST, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
this.mHeaders = headers;
}
/**
* Constructor with option method and default header configuration.
*
* @param method method for now accept POST and GET only
* @param url request destination
* @param listener on success event handler
* @param errorListener on error event handler
*/
public VolleyMultipartRequest(int method, String url,
Response.Listener<NetworkResponse> listener,
Response.ErrorListener errorListener) {
super(method, url, errorListener);
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 "multipart/form-data;boundary=" + boundary;
}
@Override
public byte[] getBody() throws AuthFailureError {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
try {
// populate text payload
Map<String, String> params = getParams();
if (params != null && params.size() > 0) {
textParse(dos, params, getParamsEncoding());
}
// populate data byte payload
Map<String, DataPart> data = getByteData();
if (data != null && data.size() > 0) {
dataParse(dos, data);
}
// close multipart form data after text and file data
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
return bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* Custom method handle data payload.
*
* @return Map data part label with data byte
* @throws AuthFailureError
*/
protected Map<String, DataPart> getByteData() throws AuthFailureError {
return null;
}
@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);
}
/**
* Parse string map into data output stream by key and value.
*
* @param dataOutputStream data output stream handle string parsing
* @param params string inputs collection
* @param encoding encode the inputs, default UTF-8
* @throws IOException
*/
private void textParse(DataOutputStream dataOutputStream, Map<String, String> params, String encoding) throws IOException {
try {
for (Map.Entry<String, String> entry : params.entrySet()) {
buildTextPart(dataOutputStream, entry.getKey(), entry.getValue());
}
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("Encoding not supported: " + encoding, uee);
}
}
/**
* Parse data into data output stream.
*
* @param dataOutputStream data output stream handle file attachment
* @param data loop through data
* @throws IOException
*/
private void dataParse(DataOutputStream dataOutputStream, Map<String, DataPart> data) throws IOException {
for (Map.Entry<String, DataPart> entry : data.entrySet()) {
buildDataPart(dataOutputStream, entry.getValue(), entry.getKey());
}
}
/**
* Write string data into header and data output stream.
*
* @param dataOutputStream data output stream handle string parsing
* @param parameterName name of input
* @param parameterValue value of input
* @throws IOException
*/
private void buildTextPart(DataOutputStream dataOutputStream, String parameterName, String parameterValue) throws IOException {
dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" + parameterName + "\"" + lineEnd);
//dataOutputStream.writeBytes("Content-Type: text/plain; charset=UTF-8" + lineEnd);
dataOutputStream.writeBytes(lineEnd);
dataOutputStream.writeBytes(parameterValue + lineEnd);
}
/**
* Write data file into header and data output stream.
*
* @param dataOutputStream data output stream handle data parsing
* @param dataFile data byte as DataPart from collection
* @param inputName name of data input
* @throws IOException
*/
private void buildDataPart(DataOutputStream dataOutputStream, DataPart dataFile, String inputName) throws IOException {
dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" +
inputName + "\"; filename=\"" + dataFile.getFileName() + "\"" + lineEnd);
if (dataFile.getType() != null && !dataFile.getType().trim().isEmpty()) {
dataOutputStream.writeBytes("Content-Type: " + dataFile.getType() + lineEnd);
}
dataOutputStream.writeBytes(lineEnd);
ByteArrayInputStream fileInputStream = new ByteArrayInputStream(dataFile.getContent());
int bytesAvailable = fileInputStream.available();
int maxBufferSize = 1024 * 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
byte[] buffer = new byte[bufferSize];
int bytesRead = fileInputStream.read(buffer, 0, bufferSize);
while (bytesRead > 0) {
dataOutputStream.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
dataOutputStream.writeBytes(lineEnd);
}
/**
* Simple data container use for passing byte file
*/
public class DataPart {
private String fileName;
private byte[] content;
private String type;
/**
* Default data part
*/
public DataPart() {
}
/**
* Constructor with data.
*
* @param name label of data
* @param data byte data
*/
public DataPart(String name, byte[] data) {
fileName = name;
content = data;
}
/**
* Constructor with mime data type.
*
* @param name label of data
* @param data byte data
* @param mimeType mime data like "image/jpeg"
*/
public DataPart(String name, byte[] data, String mimeType) {
fileName = name;
content = data;
type = mimeType;
}
/**
* Getter file name.
*
* @return file name
*/
public String getFileName() {
return fileName;
}
/**
* Setter file name.
*
* @param fileName string file name
*/
public void setFileName(String fileName) {
this.fileName = fileName;
}
/**
* Getter content.
*
* @return byte file data
*/
public byte[] getContent() {
return content;
}
/**
* Setter content.
*
* @param content byte file data
*/
public void setContent(byte[] content) {
this.content = content;
}
/**
* Getter mime type.
*
* @return mime type
*/
public String getType() {
return type;
}
/**
* Setter mime type.
*
* @param type mime type
*/
public void setType(String type) {
this.type = type;
}
}
}
package com.sketchproject.infogue.modules;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;
/**
* Singleton volley to populate request into single queue.
*
* Sketch Project Studio
* Created by Angga on 22/04/2016 22.58.
*/
public class VolleySingleton {
private static VolleySingleton mInstance;
private RequestQueue mRequestQueue;
private ImageLoader mImageLoader;
private static Context mCtx;
/**
* Private constructor, only initialization from getInstance.
*
* @param context parent context
*/
private VolleySingleton(Context context) {
mCtx = context;
mRequestQueue = getRequestQueue();
mImageLoader = new ImageLoader(mRequestQueue,
new ImageLoader.ImageCache() {
private final LruCache<String, Bitmap> cache = new LruBitmapCache(mCtx);
@Override
public Bitmap getBitmap(String url) {
return cache.get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
cache.put(url, bitmap);
}
});
}
/**
* Singleton construct design pattern.
*
* @param context parent context
* @return single instance of VolleySingleton
*/
public static synchronized VolleySingleton getInstance(Context context) {
if (mInstance == null) {
mInstance = new VolleySingleton(context);
}
return mInstance;
}
/**
* Get current request queue.
*
* @return RequestQueue
*/
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
// getApplicationContext() is key, it keeps you from leaking the
// Activity or BroadcastReceiver if someone passes one in.
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
}
return mRequestQueue;
}
/**
* Add new request depend on type like string, json object, json array request.
*
* @param req new request
* @param <T> request type
*/
public <T> void addToRequestQueue(Request<T> req) {
getRequestQueue().add(req);
}
/**
* Get image loader.
*
* @return ImageLoader
*/
public ImageLoader getImageLoader() {
return mImageLoader;
}
}
Owner

anggadarkprince commented Apr 27, 2016 edited

After read bunch of articles and stack overflow questions, I tried wrap up solution and it works perfectly, thanks to all indirect contributors, I rewrite their code, more modular and easy to use. All related code inspired by

http://stackoverflow.com/questions/32240177/working-post-multipart-request-with-volley-and-without-httpentity
http://stackoverflow.com/questions/16797468/how-to-send-a-multipart-form-data-post-in-android-with-volley
http://stackoverflow.com/questions/32187450/trouble-sending-multipart-file-with-boundary-via-volley

try and give me feedback.

ray86 commented May 4, 2016

came across your gist while searching on the web. i have a question though, how is the image sent to a server? in byte array or base64?
if possible how can i retrieve and save the image in a php server. thanks

aldo123 commented May 5, 2016 edited

I came a cross your gist on stackover flow, I must say its well done, it has really eased my work. Many thanks

aldo123 commented May 5, 2016

ray86 is the normal way you handle multipart data from a normal form on the web. I am using REST Api library on code igniter framework.

Owner

anggadarkprince commented May 8, 2016 edited

@ray86 No, this code does not convert images or file into string, you just treat the file like web form,, the idea build multipart data header,, it's similar you upload file as well..

inlacou commented May 31, 2016 edited

First of all, thanks. Great code!

Now my question: Is there any way to send multiple files at once with the same name?

@inlacou you can change file name in php code.

danponce commented Jun 7, 2016

this is great @anggadarkprince !! I have one question, can you add the LruBitmapCache.java?

datafile.getContent() is returning null to me

i need progress count for inform user there is any idea to do this inside VolleyMultipartRequest.java

db0910 commented Jun 19, 2016 edited

Dear
@anggadarkprince
thank U
perfect code
very easily to apply
before Ur solution
I'm using Base64encode file Volley PostReuest upload and Server side decode it

but there is a little problem for me
It seems that textpart doesn't support Traditional-Chinese
for example:
params.put("tanktype", "男版"); the server side $_POST["tanktype"] got "7H"
any suggestion?

BTW
@danponce
just google "LruBitmapCache"
a sample implementation for an in-memory LruBitmapCache class FOUND
https://developer.android.com/training/volley/request.html

Owner

anggadarkprince commented Jun 21, 2016

@db0910 try to uncomment the encoding code
dataOutputStream.writeBytes("Content-Type: text/plain; charset=UTF-8" + lineEnd);
maybe it help set UTF-8 encoding that support Traditional-Chinese

Owner

anggadarkprince commented Jun 21, 2016

@abdulahad1991 you must passing byte data via setter method or constructor, i just give an example retrieve image byte data from drawable content, please explore how you could improvise depends on your cases..

db0910 commented Jun 26, 2016

Dear @anggadarkprince
thanks for your reply
I have solved this problem (UTF-8 encode sequence)

In the "buildTextPart" subroutine
change
"dataOutputStream.writeBytes(parameterValue + lineEnd);"

to
"dataOutputStream.write(parameterValue.getBytes("utf-8"));"
"dataOutputStream.writeBytes(lineEnd);"

and I don't uncomment the encoding code
"dataOutputStream.writeBytes("Content-Type: text/plain; charset=UTF-8" + lineEnd);"

thanks for yout perfect code
very easily to apply
thank you

Owner

anggadarkprince commented Jul 3, 2016

@db0910 oh i see, i'm glad for you too you found the solution,
I had the same problem before about uploaded image, until i rewrote the code from many references, and here it is....

can we determine progress of request

joknu1 commented Oct 4, 2016

Thank you for showing how to do this.
Is there a license tied to this? May I use your design of VolleyMultipartRequest.java and would you like credit?

w4lle commented Oct 27, 2016

@anggadarkprince
great code!
@db0910
thank you for sharing this problem.

po5i commented Nov 21, 2016

Awesome code. This is gold.

I used your gist with some editions.
It sends data fine.
But I have a problem with encoding.
I use Arabic text but it is sent as English text.
Any suggestions.?

@db0910 Thank you for this.
"dataOutputStream.write(parameterValue.getBytes("utf-8"));"
"dataOutputStream.writeBytes(lineEnd);"

DeveloperMCD commented Dec 20, 2016 edited

How could we send data from a phone camera intent, or a file on the phone? Your only example (it seems) is getting the byte data from a "drawable". Is that really the most common use case of this code? I admit I'm new to posting multipart data over the network in Android.
In other words, what other classes could you make like "AppHelper.getFileDataFromDrawable", that get data from a file for example?

Would it start with sending in file_name as a String, and then:
File file = new File(file_name);
FileBody fb = new FileBody(file);
??

arvi commented Dec 22, 2016 edited

@anggadarkprince I'm new to android development and would like to ask if this code can handle audio or video stream to upload to server? Thanks a lot 😄

to answer my own question... yep, it can support audio and video 😄

@db0910 - Thanks! you saved my day.

stest309 commented Jan 27, 2017 edited

Hi nice description. When I tried with this by sending json in value of hashMap I got multipart boundry not supported. I do not understand is there any problem at app side to send or to receive at back end side. So please can you send how to receive json & multipart body at server side?

@db0910 Thank you so much!

Thank you !!!

Awsom work

I have issues on how to receive the file at my server side. can anyone help me with a php sample?

quadrixm commented Mar 1, 2017

Its good for image file. But can't get for uploading videos. How will video file will get uploaded from VideoView?

Thank you, it works for me

LeonardoAfonso commented Mar 28, 2017 edited

Hi... I liked your code and it worked very well for me. But I wanted to get files stored on External Storage, so I changed to get files via Uri.

public static byte[] getFileDataFromUri(Context context, Uri uri) { Bitmap bitmap = null; try { bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri); } catch (IOException e) { e.printStackTrace(); } ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 80, byteArrayOutputStream); return byteArrayOutputStream.toByteArray(); }

After searching for what felt like a lifetime, I found this post and it worked like a charm. All I can say is THANK YOU

Ahsan721 commented May 2, 2017

can i upload audia, video and multiple images using this code??

Just wanted to leave a big thanks for sharing. This really helped want I wanted to do.
And btw, as some users are also requesting, this would be even better if it worked with any filetype.
Again, thank you.

krunal3kapadiya commented May 9, 2017 edited

I got this NoConnectionError: java.io.IOException: unexpected end of stream on Connection, while uploading image and video. What could be the possible solution? Here is the more detail about the problem http://stackoverflow.com/q/43977319/5255006.

@anggadarkprince , THANK YOU A LOT! First Really Working Solution which was worked for me. You solved my biggest problem. Thanx!

qaisershehzad commented Jun 14, 2017 edited

Just wanted to check if anyone is able to upload video from Uri? If possible please share some sample.

able to do by converting to ByteArray

`ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileInputStream fis = new FileInputStream(new File(mImageUri.getPath()));

                byte[] buf = new byte[1024];
                int n;
                while (-1 != (n = fis.read(buf)))
                    baos.write(buf, 0, n);

                byte[] videoBytes = baos.toByteArray();`

Many thanks for your code! It has saved my day!

DChipolla commented Aug 2, 2017 edited

Thank you, Thank you, Thank you...... New to Java and Android world and after many attempts to transmit image to service with no success, this has help more than you can imagine, works like a charm...

nirukk52 commented Aug 9, 2017

hello, I am getting an error on this line. Cannot resolve symbol "LruBitmapCache"
please help,
private final LruCache<String, Bitmap> cache = new LruBitmapCache(mCtx);

Thanks in advance

mosazaid commented Aug 14, 2017 edited

@db0910 and @anggadarkprince thank you very much for your support and your code

you two really saved my day , thankkkkkkkkkkkkkk youuuuuuuuuuu :*

Thanks a lot

is this code will upload pdf document as well??

esryvz commented Sep 8, 2017

Hi this request work in activity but doesn't work in Fragment. How can i do in Fragment ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment