Skip to content

Instantly share code, notes, and snippets.

@anggadarkprince
Last active May 2, 2023 22:39
Show Gist options
  • Save anggadarkprince/a7c536da091f4b26bb4abf2f92926594 to your computer and use it in GitHub Desktop.
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;
}
}
@abhinavKeshri07
Copy link

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

Thank you so much!

@zionnite
Copy link

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

@Abdelhady
Copy link

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

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

@Mr-Ramzan
Copy link

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

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

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

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

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

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

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

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

@sharpvisions
Copy link

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

Thanks for the answer you saved my life...

@dneykov
Copy link

dneykov commented Apr 23, 2021

@db0910 Thanks for your addition to VolleyMultipartRequest

@tosintech-web
Copy link

Thank you! It works for me very well!

@abhayst007
Copy link

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

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

@pappu912896
Copy link

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