Skip to content

Instantly share code, notes, and snippets.

@anggadarkprince
Last active May 2, 2023 22:39
Show Gist options
  • Select an option

  • Save anggadarkprince/a7c536da091f4b26bb4abf2f92926594 to your computer and use it in GitHub Desktop.

Select an option

Save anggadarkprince/a7c536da091f4b26bb4abf2f92926594 to your computer and use it in GitHub Desktop.
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", mAboutInput.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;
}
}
@rajansaumya
Copy link
Copy Markdown

works like a charm thank you!!

@matingdz
Copy link
Copy Markdown

thanks a lot. I didn't use it for uploading an image, I used it to send data to a server and it worked fine.

@ajinkya5130
Copy link
Copy Markdown

Hello Sir i want backend code of PHP(webservice) through which images are going to upload to server

@nayan-dabhi
Copy link
Copy Markdown

nayan-dabhi commented Nov 28, 2018

Awesome code. Thanks a lot.
For single file upload done successfully.

@anggadarkprince : Is there any way to send multiple files at once with the same name?

Because currently it is not working with same keyname.

e.g.

protected Map<String, DataPart> getByteData() {
    Map<String, DataPart> params = new HashMap<>();

    params.put("avatar", new DataPart("file_avatar.jpg", AppHelper.getFileDataFromDrawable(getBaseContext(), mAvatarImage.getDrawable()), "image/jpeg"));
    params.put("avatar", new DataPart("file_cover.jpg", AppHelper.getFileDataFromDrawable(getBaseContext(), mCoverImage.getDrawable()), "image/jpeg"));

    return params;
}

Please provide any solution for this problem.

@mustafamn
Copy link
Copy Markdown

how to send pdf file please anyone help me i m getting size = 0 while uploading file to server any one show the code to send pdf file

@deansg
Copy link
Copy Markdown

deansg commented Dec 16, 2018

This code works really well, however I have only a small issue with it:
When I send binary data, your code adds quotation marks to the name & file-name, which creates a problem for me. I think a better solution would be to only escape the quotation marks inside the sent names/filenames if they exist, and leave out the added quotation marks.

This can be done by replacing the second line in the "buildDataPart" method with the following:

dataOutputStream.writeBytes(String.format( "Content-Disposition: form-data; name=%s;filename=%s%s", inputName, dataFile.getFileName(), lineEnd));

@ufocom
Copy link
Copy Markdown

ufocom commented Dec 19, 2018

I have huge problem and i don't know how to fix it. My server is looking for File=file(in postman when i make call i have to put key "File" and value is file) how i Can send it? In multipart was something like builder.addFormDataPart("file", "image.jpg", RequestBody.create(MediaType.parse("image/jpeg"), byteArray.toByteArray())); But there is no such thinks ;/

@Th3Ov3r
Copy link
Copy Markdown

Th3Ov3r commented Mar 15, 2019

I have a problem with words with accents, for example mamá, It gives an error if I send that, that's why what I'm doing is removing that accent. ¿Is there any solution for this?

@ankurupadhyay123
Copy link
Copy Markdown

you didn't set content type in constructor of DataPart, thus content type is null

@nic0latesla
Copy link
Copy Markdown

Great code... Thank you

@ak010cse
Copy link
Copy Markdown

ak010cse commented May 8, 2019

Getting an error in :
if (status.equals(Constant.REQUEST_SUCCESS))
Error : Cannot resolve symbol

Do I need to create a "Constant" class?

@MustafaAndroid
Copy link
Copy Markdown

Hi I used this working perfectly but on P is not working any help will me more helpful

@abhinavKeshri07
Copy link
Copy Markdown

abhinavKeshri07 commented Sep 13, 2019

Awesome code. This works like a charm and saved a lot of my time. Thanks a lot.

@klgelvez
Copy link
Copy Markdown

Thank you so much!

@zionnite
Copy link
Copy Markdown

How can i retrieve the send data via my php code??

@Abdelhady
Copy link
Copy Markdown

I made a fork based on the good answer by @db0910 to support UTF-8 .. Thanks @anggadarkprince for providing the gist in the first place 👍 😉

@zionnite
Copy link
Copy Markdown

Hey how do i get the send data via the php ??

@Mr-Ramzan
Copy link
Copy Markdown

Does anyone have an issue with emojis?
I am sending emojis in the Text part that is not being sent as are.
like 😂 ===>"=\u0002"
Please help I anyone knows the issue

@Abdelhady
Copy link
Copy Markdown

Does anyone have an issue with emojis?
I am sending emojis in the Text part that is not being sent as are.
like ===>"=\u0002"
Please help I anyone knows the issue

@Mr-Ramzan it might be a utf-8 issue, not sure, but try the fork I made earlier to handle the utf-8 encoding

@imfreco
Copy link
Copy Markdown

imfreco commented Mar 23, 2020

Hola amigo @anggadarkprince estoy tratando de adaptar la clase VolleyMultipart a una llamada VolleyArrayMultipart para poder subir un array de archivos. Te agradezco de antemano si podrías darme algunas indicaciones.

@janakthummar
Copy link
Copy Markdown

Thank You For this code.But i am getting this message.can any one help me.
if (status.equals(Constant.REQUEST_SUCCESS))
Error : Cannot resolve symbol

@ethichadebe
Copy link
Copy Markdown

Getting an error in :
if (status.equals(Constant.REQUEST_SUCCESS))
Error : Cannot resolve symbol

Do I need to create a "Constant" class?

replace "Constant.REQUEST_SUCCESS" that with the success message returned by your server

@ethichadebe
Copy link
Copy Markdown

Bro, you're an actual lifesaver, I've looked everywhere for this, Such a clean and well-commented solution, you're amazing

@sahilsinghss73
Copy link
Copy Markdown

Hey, thank you so much.
Just a minor correction:

at MainActivity.java, line 98
replace "mAvatarInput" with "mAboutInput"

@sharpvisions
Copy link
Copy Markdown

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

Thanks for the answer you saved my life...

@dneykov
Copy link
Copy Markdown

dneykov commented Apr 23, 2021

@db0910 Thanks for your addition to VolleyMultipartRequest

@tosintech-web
Copy link
Copy Markdown

Thank you! It works for me very well!

@abhayst007
Copy link
Copy Markdown

in class VolleySingleton.java
private final LruCache<String, Bitmap> cache = new LruBitmapCache(mCtx);
in this line i am getting error that
cannot resolve symbol LruBitmapCache

@sharjaassetmanagment
Copy link
Copy Markdown

i am getting this in respnse, what could be the issue
com.android.volley.NetworkResponse@ed190b5

@pappu912896
Copy link
Copy Markdown

Thank you so much it's work for me

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