This is the gist iam refering for uploading images to the server https://gist.github.com/anggadarkprince/a7c536da091f4b26bb4abf2f92926594
But iam using TedPicker OnMultiImageSelectedListener to select multiple images https://android-arsenal.com/details/1/4320
It gives me selectedUriList in an ArrayLIst . I convert all the Uri's to byteArray and upload to server.
Here is my Code of Uploading Image .
private void imageUpload() {
final ProgressDialog loading = ProgressDialog.show(this,"Uploading...","Please wait...",false,false);
VolleyMultipartRequest multipartRequest = new VolleyMultipartRequest(Request.Method.POST, Constants.UPLOAD_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");
loading.dismiss();
if (status.equals(Constants.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) {
loading.dismiss();
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);
}
}) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("folder_name", FOLDER_NAME);
return params;
}
#Override
protected Map<String, DataPart> getByteData() {
Map<String, DataPart> params = new HashMap<>();
int i = 0;
for (Uri uri : selectedUriList) {
i++;
try {
InputStream iStream = getContentResolver().openInputStream(uri);
byte[] inputData = getBytes(iStream);
params.put("image_file"+i, new DataPart("image"+i+".jpg", inputData , "image/jpeg"));
} catch (IOException e) {
e.printStackTrace();
}
}
return params;
}
};
VolleySingleton.getInstance(getBaseContext()).addToRequestQueue(multipartRequest);
}
public byte[] getBytes(InputStream inputStream) throws IOException {
ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int len = 0;
while ((len = inputStream.read(buffer)) != -1) {
byteBuffer.write(buffer, 0, len);
}
return byteBuffer.toByteArray();
}
The error iam facing while trying to upload image
I/System.out: [socket]rx timeout:2500
I/System.out: [socket][0] connection ascentcity.com/119.81.195.196:80;LocalPort=45296(2500)
I/System.out: [CDS]connect[ascentcity.com/119.81.195.196:80] tm:2
D/Posix: [Posix_connect Debug]Process com.vst.image.vehiclestimageclassifier :80
I/System.out: [socket][/192.168.31.42:45296] connected
I/System.out: [socket]rx timeout:2500
D/Volley: [22832] BasicNetwork.logSlowRequests: HTTP response for request=<[ ] http://ascentcity.com/Mobileapp/upload.php 0x43980958 NORMAL 1> [lifetime=48143], [size=116], [rc=200], [retryCount=0]
W/System.err: org.json.JSONException: Value Sorry of type java.lang.String cannot be converted to JSONObject
W/System.err: at org.json.JSON.typeMismatch(JSON.java:111)
W/System.err: at org.json.JSONObject.<init>(JSONObject.java:160)
W/System.err: at org.json.JSONObject.<init>(JSONObject.java:173)
W/System.err: at com.vst.image.vehiclestimageclassifier.MainActivity$3.onResponse(MainActivity.java:171)
W/System.err: at com.vst.image.vehiclestimageclassifier.MainActivity$3.onResponse(MainActivity.java:166)
W/System.err: at com.vst.image.vehiclestimageclassifier.VolleyMultipartRequest.deliverResponse(VolleyMultipartRequest.java:127)
W/System.err: at com.vst.image.vehiclestimageclassifier.VolleyMultipartRequest.deliverResponse(VolleyMultipartRequest.java:24)
W/System.err: at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ExecutorDelivery.java:99)
W/System.err: at android.os.Handler.handleCallback(Handler.java:739)
W/System.err: at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err: at android.os.Looper.loop(Looper.java:157)
W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5429)
W/System.err: at java.lang.reflect.Method.invoke(Native Method)
W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
I have a few questions
Is it okay to retrive byte data from images like this or there are better ways ?
#Override
protected Map<String, DataPart> getByteData() {
Map<String, DataPart> params = new HashMap<>();
int i = 0;
for (Uri uri : selectedUriList) {
i++;
try {
InputStream iStream = getContentResolver().openInputStream(uri);
byte[] inputData = getBytes(iStream);
// file name could found file base or direct access from real path
// for now just get bitmap data from ImageView
params.put("image_file"+i, new DataPart("image"+i+".jpg", inputData , "image/jpeg"));
} catch (IOException e) {
e.printStackTrace();
}
}
return params;
}
The error iam facing is due to slow server ? or something else ? how to debug this?
Is there something i need to communicate to backend team (Server Side) , imean to handle my request .
Here is the github link to complete project code https://github.com/sooorajjj/vstImageClassifier
Thanks!
Here is my customised VolleyMultipartRequest file. You may use it directly for uploading multiple images in same parameter name :
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 org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Map;
/**
* Custom request to make multipart header and upload file.
* <p>
* Sketch Project Studio
* Created by bhuvnesh 8/10/2019
*/
#SuppressWarnings("unused")
public class VolleyMultipartRequest extends Request<JSONObject> {
private final String twoHyphens = "--";
private final String lineEnd = "\r\n";
private final String boundary = "apiclient-" + System.currentTimeMillis();
private Response.Listener<JSONObject> mListener;
private Response.ErrorListener mErrorListener;
private Map<String, String> mHeaders;
public VolleyMultipartRequest(String url, Map<String, String> headers,
Response.Listener<JSONObject> listener,
Response.ErrorListener errorListener) {
super(Method.POST, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
this.mHeaders = headers;
}
public VolleyMultipartRequest(int method, String url,
Response.Listener<JSONObject> 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, ArrayList<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;
}
protected Map<String, ArrayList<DataPart>> getByteData() throws AuthFailureError {
return null;
}
#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) {
mListener.onResponse(response);
}
#Override
public void deliverError(VolleyError error) {
mErrorListener.onErrorResponse(error);
}
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);
}
}
private void dataParse(DataOutputStream dataOutputStream, Map<String, ArrayList<DataPart>> data) throws IOException {
for (Map.Entry<String, ArrayList<DataPart>> entry : data.entrySet()) {
buildDataPart(dataOutputStream, entry.getValue(), entry.getKey());
}
}
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);
}
private void buildDataPart(DataOutputStream dataOutputStream, ArrayList<DataPart> dataFile, String inputName) throws IOException {
for (int i=0; i<dataFile.size(); i++){
dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
DataPart dp = dataFile.get(i);
dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" +
inputName + "\"; filename=\"" + dp.getFileName() + "\"" + lineEnd);
if (dp.getType() != null && !dp.getType().trim().isEmpty()) {
dataOutputStream.writeBytes("Content-Type: " + dp.getType() + lineEnd);
}
dataOutputStream.writeBytes(lineEnd);
ByteArrayInputStream fileInputStream = new ByteArrayInputStream(dp.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);
}
}
public class DataPart {
private String fileName;
private byte[] content;
private String type;
public DataPart() {
}
public DataPart(String name, byte[] data) {
fileName = name;
content = data;
}
public DataPart(String name, byte[] data, String mimeType) {
fileName = name;
content = data;
type = mimeType;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
}
And here is code to use this volleyMultipartRequest file :
VolleyMultipartRequest multipartRequest = new VolleyMultipartRequest(Request.Method.POST,
BaseURL.ADD_INVOICE, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
Toast.makeText(HomeActivity.this, response.getString("message"), Toast.LENGTH_LONG).show();
if (response.getString("message").equals("Invoice added successfully!!")){
check=true;
Intent intent = new Intent(HomeActivity.this, ThankyouActivity.class);
startActivity(intent);
//finish();
}else {
Toast.makeText(HomeActivity.this, response.getString("message"), Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
progressdialog.dismisswaitdialog();
if (error instanceof TimeoutError || error instanceof NoConnectionError) {
Toast.makeText(HomeActivity.this, getResources().getString(R.string.connection_time_out), Toast.LENGTH_SHORT).show();
}
}
}) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("user_id", user_id);
params.put("year", et_year.getText().toString());
params.put("make", et_make.getText().toString());
params.put("model", et_model.getText().toString());
return params;
}
#Override
protected Map<String, ArrayList<DataPart>> getByteData() {
Map<String, VolleyMultipartRequest.DataPart> params = new HashMap<>();
Map<String, ArrayList<DataPart>> imageList = new HashMap<>();
ArrayList<DataPart> dataPart = new ArrayList<>();
String imagename = "image[]";
for (int i=0; i<encodedImageList.size(); i++){
VolleyMultipartRequest.DataPart dp = new VolleyMultipartRequest.DataPart(imagename+i, Base64.decode(encodedImageList.get(i), Base64.DEFAULT), "image/jpeg");
dataPart.add(dp);
//params.put(imagename, new DataPart(imagename+i, Base64.decode(encodedImageList.get(i), Base64.DEFAULT), "image/jpeg"));
}
imageList.put("image[]", dataPart);
return imageList;
}
};
int socketTimeout = 500000;//30 seconds - change to what you want
RetryPolicy policy = new DefaultRetryPolicy(socketTimeout, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
multipartRequest.setRetryPolicy(policy);
AppController.getInstance().addToRequestQueue(multipartRequest);
For Kotlin use MutableMap<String,ArrayList<DataPart>> instead of Map<String,ArrayList<DataPart>>.
And should be initialize like this way.
val dataParts:MutableMap<String,ArrayList<DataPart>> = mutableMapOf()
And put the data same as HashMap<K,V>.
Related
These my PHP Script Very well work check using postman upload photo successfully
But Problem is not Working on site code check my code its working efficient
postman outman working find in thse case android why not proper send data on serger if you guise are help they my work verywell done please please help it
require_once "connection.php";
$upload_path = 'upload/';
$server_ip = gethostbyname(gethostname());
$upload_url = 'http://'.$server_ip.'/shopping/'.$upload_path;
$responce = array();
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
if(isset($_POST['name']) and isset($_FILES['image']['name']))
{
global $connect;
$name = $_POST['name'];
$fileinfo = pathinfo($_FILES['image']['name']);
$extension = $fileinfo['extension'];
$file_url = $upload_url . getFileName() . '.'. $extension;
$file_path = $upload_path . getFileName() . '.'. $extension;
try{
move_uploaded_file($_FILES['image']['tmp_name'],$file_path);
$query = "INSERT INTO image(url,name) VALUES('$file_url','$name');";
mysqli_query($connect,$query);
{
$responce['error'] = false;
$responce['url'] = $file_url;
$responce['name'] = $name;
}
}
catch(Exception $e)
{
$responce['error'] = false;
$responce['message']=$e->getMessage();
}
//mysqli_close($connect);
}
else
{
$responce['error'] = true;
$responce['message'] = 'please choose file';
}
echo json_encode($responce);
}
function getFileName()
{
global $connect;
$sql = "SELECT MAX(id) as id FROM image";
$result = mysqli_fetch_array(mysqli_query($connect,$sql));
if($result['id'] == null)
{
return 1;
}
else
{
return ++$result['id'];
}
mysqli_close($connect);
}
?>
Php Code
These is My Android code please help me to solve these problem
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
ImageView imageAdd;
EditText name;
Button select,upload;
private Uri FilePath;
private Bitmap bitmap;
private String UPLOAD_URL = "http://192.168.0.103/shopping/file_upload.php";
private static final int STORAGE_PERMISSION_CODE = 2210;
private static final int PICK_IMAGE_REQUEST = 2310;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageAdd = findViewById(R.id.imageAdd);
name = findViewById(R.id.name);
select = findViewById(R.id.select);
upload = findViewById(R.id.upload);
select.setOnClickListener(this);
upload.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if (v == upload){
uploadImage();
}
if (v == select){
ShowFileChooser();
}
}
//Open Dialog For Choose Images
private void ShowFileChooser(){
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,"Select Picture"),PICK_IMAGE_REQUEST);
}
//Showing Result in ImageView
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null){
FilePath = data.getData();
try {
bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(),FilePath);
imageAdd.setImageBitmap(bitmap);
} catch (IOException e) {
e.printStackTrace();
}
}
} //over these method
private void uploadImage() {
final String NAME = name.getText().toString().trim();
StringRequest stringRequest = new StringRequest(Request.Method.POST, UPLOAD_URL, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jsonObject = new JSONObject(response);
Toast.makeText(MainActivity.this, jsonObject.getString(response), Toast.LENGTH_LONG).show();
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(MainActivity.this,error.getMessage(),Toast.LENGTH_LONG).show();
}
}) {
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> params = new HashMap<>();
params.put("url",convertBitmapToString(bitmap));
params.put("name",NAME);
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(stringRequest);
}
public String convertBitmapToString(Bitmap bmp) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, 100, stream); //compress to which format you want.
byte[] byte_arr = stream.toByteArray();
String imageStr = Base64.encodeToString(byte_arr, 1);
return imageStr;
}
you are using Base64 formate to upload which is not efficient way Case encoded Base64 string's length increase more than original one . You can upload image both Base64 way and Multi part way .I prefer multi-part process . I think below snap-net code gonna help you.
Create
VolleyMultipartRequest.class
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;
}
}
}
You can implement in activity or fragment like this
private void saveDoctorProfile(final String doctorId, final String firstName, final String lastName) {
showDialog();
VolleyMultipartRequest multipartRequest = new VolleyMultipartRequest(Request.Method.POST, AppConfig.LIVE_API_LINK + "getDoctorProfilePersonalinfo"
, new Response.Listener<NetworkResponse>() {
#Override
public void onResponse(NetworkResponse response) {
closeDialog();
String resultResponse = new String(response.data);
Log.d("doctor_profile_save", resultResponse);
try {
JSONObject result = new JSONObject(resultResponse);
AlertDialogManager.showSuccessDialog(getActivity(), "Your Personal Information saved successfully");
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
closeDialog();
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("dri_drid", doctorId);
params.put("first_name", firstName);
params.put("last_name", lastName);
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("image", new DataPart("file_avatar.jpg", AppHelper.getFileDataFromDrawable(getActivity(), ivPatientProfile.getDrawable()), "image/jpeg"));
return params;
}
};
AppController.getInstance().addToRequestQueue(multipartRequest);
}
I have an image and I want to upload this image to my web service using Volley library, the problem is I'm looking for a how to do it but still haven't found.
I am trying to upload image with Volley Multipart Request but I am not get success response from server but when I upload image using postman it's return success. This is my postman screenshot.
I search relate this but not get any idea.
below is my code
private void uploadMedia(final String token) {
VolleyMultipartRequest volleyMultipartRequest = new VolleyMultipartRequest(Request.Method.POST, upload_image,
new Response.Listener<NetworkResponse>() {
#Override
public void onResponse(NetworkResponse response) {
// iv_imageview.setImageBitmap(bitmap);
Log.e("VolleyOnResponse200", response.toString());
try {
JSONObject obj = new JSONObject(new String(response.data));
Log.e("response",obj+"");
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#SuppressLint("LongLogTag")
#Override
public void onErrorResponse(VolleyError error) {
Log.e("VolleyonErrorResponse200", "Error: " + error.getMessage());
// Toasty.error(getApplicationContext(),"Something went wrong", Toast.LENGTH_SHORT).show();
NetworkResponse response = error.networkResponse;
if (error instanceof ServerError && response != null) {
try {
String res = new String(response.data,
HttpHeaderParser.parseCharset(response.headers, "utf-8"));
// Now you can use any deserializer to make sense of data
JSONObject obj = new JSONObject(res);
} catch (UnsupportedEncodingException e1) {
// Couldn't properly decode data to string
e1.printStackTrace();
} catch (JSONException e2) {
// returned data is not JSONObject?
e2.printStackTrace();
}
}
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "bearer " + token);
return params;
}
#Override
protected Map<String, DataPart> getByteData() {
Map<String, DataPart> params = new HashMap<>();
long imagename = System.currentTimeMillis();
params.put("image", new DataPart(imagename + ".jpg", getFileDataFromDrawable(bitmap)));
return params;
}
};
volleyMultipartRequest.setRetryPolicy(new DefaultRetryPolicy(
999999999,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
//adding the request to volley
Volley.newRequestQueue(this).add(volleyMultipartRequest);
}
public byte[] getFileDataFromDrawable(Bitmap bitmap) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 80, byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
}
This is my VolleyMultipartRequest class
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;
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(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);
}
public class DataPart {
private String fileName;
private byte[] content;
private String type;
public DataPart(String selectedPath, String path) {
}
public DataPart(String name, byte[] data) {
fileName = name;
content = data;
}
String getFileName() {
return fileName;
}
byte[] getContent() {
return content;
}
String getType() {
return type;
}
}
}
see below code I think there is problem
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json; charset=UTF-8");
params.put("Authorization", "bearer " + token);
return params;
}
please comment below line
params.put("Content-Type", "application/json; charset=UTF-8");
This will help you
I'm just beginner in Android App development.
I want to upload image files using Android Volley library.
I have used this helper class found somewhere in some blog.:
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;
SharedPreferencesManager sharedPreferencesManager =null;
public VolleyMultiPartRequest(int method, String url,
Response.Listener<NetworkResponse> listener,
Response.ErrorListener errorListener, Context context) {
super(method, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
this.sharedPreferencesManager = new SharedPreferencesManager(context);
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<String, String>();
headers.put("Accept", "application/json");
headers.put("Authorization", "Bearer " + sharedPreferencesManager.retreiveString(AppText.ACCESS_TOKEN));
headers.put("Content-Type", "multipart/form-data");
return headers;
}
#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;
}
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);
}
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);
}
}
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());
}
}
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(lineEnd);
dataOutputStream.writeBytes(parameterValue + lineEnd);
}
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);
}
public class DataPart {
private String fileName;
private byte[] content;
private String type;
public DataPart() {
}
public DataPart(String name, byte[] data) {
fileName = name;
content = data;
}
String getFileName() {
return fileName;
}
byte[] getContent() {
return content;
}
String getType() {
return type;
}
}
}
And called this helper like this:
VolleyMultiPartRequest volleyMultipartRequest = new VolleyMultiPartRequest(Request.Method.POST, Api.UPLOAD_FILE,
new Response.Listener<NetworkResponse>() {
#Override
public void onResponse(NetworkResponse response) {
try {
JSONObject obj = new JSONObject(new String(response.data));
Toast.makeText(context, obj.getString("message"), Toast.LENGTH_SHORT).show();
successCallBack.onSuccess(obj);
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(context, error.getMessage(), Toast.LENGTH_SHORT).show();
try {
errorCallBack.onError(new JSONObject(""));
} catch (JSONException e) {
e.printStackTrace();
}
}
}, context) {
/*
* If you want to add more parameters with the image
* you can do it here
* here we have only one parameter with the image
* which is tags
* */
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put("file_for", "post");
params.put("file_type", "image");
return params;
}
/*
* Here we are passing image by renaming it with a unique name
* */
#Override
protected Map<String, DataPart> getByteData() {
Map<String, DataPart> params = new HashMap<>();
long imagename = System.currentTimeMillis();
params.put("images[]", new DataPart(imagename + ".png", getFileDataFromDrawable(bitmap)));
return params;
}
};
I'm getting server error as a response.
I tried different tutorials but didn't find the exact way and easy solution.
In the postman, headers and params are like this, there are an array of images, you can see in photos attached (8 photos). I want to do exact same API call using volley library.
Try this code:
I had similar kind of issue i fixed using this
Uploading multiple files in array using multipart
Add dependecies
compile 'org.apache.httpcomponents:httpcore:4.2.4'
compile 'org.apache.httpcomponents:httpmime:4.2'
compile 'com.mcxiaoke.volley:library:1.0.19'
2.Add in gradle
defaultConfig {
useLibrary 'org.apache.http.legacy'
}
Helper class AndroidMultiPartEntity.java
public class AndroidMultiPartEntity extends MultipartEntity
{
private final ProgressListener listener;
public AndroidMultiPartEntity(final ProgressListener listener) {
super();
this.listener = listener;
}
public AndroidMultiPartEntity(final HttpMultipartMode mode, final ProgressListener listener) {
super(mode);
this.listener = listener;
}
public AndroidMultiPartEntity(HttpMultipartMode mode, final String boundary,
final Charset charset, final ProgressListener listener) {
super(mode, boundary, charset);
this.listener = listener;
}
#Override
public void writeTo(final OutputStream outstream) throws IOException {
super.writeTo(new CountingOutputStream(outstream, this.listener));
}
public interface ProgressListener {
void transferred(long num);
}
public static class CountingOutputStream extends FilterOutputStream {
private final ProgressListener listener;
private long transferred;
public CountingOutputStream(final OutputStream out,
final ProgressListener listener) {
super(out);
this.listener = listener;
this.transferred = 0;
}
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
this.transferred += len;
this.listener.transferred(this.transferred);
}
public void write(int b) throws IOException {
out.write(b);
this.transferred++;
this.listener.transferred(this.transferred);
}
}
}
API call method
private class registerCall extends AsyncTask<Void, Integer, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onProgressUpdate(Integer... progress) {
}
#Override
protected String doInBackground(Void... params) {
return registerMultipartCall();
}
private String registerMultipartCall() {
String responseString = null;
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("URL"); //UPLOAD URL
try {
AndroidMultiPartEntity entity = new AndroidMultiPartEntity(new AndroidMultiPartEntity.ProgressListener() {
#Override
public void transferred(long num) {
}
});
File sourceFile = new File(filePath);
File sourceFile2 = new File(filePath);
entity.addPart("images[0]", new FileBody(sourceFile));
entity.addPart("images[1]", new FileBody(sourceFile2));
//Do your stuff multiple files
entity.addPart("file_type", new StringBody("image");
entity.addPart("file_form", new StringBody("post");
httppost.addHeader("Accept","application/json");
httppost.addHeader("Content-Type", "multipart/form-data");
httppost.addHeader("Authorization", "Bearer " + sharedPreferencesManager.retreiveString(AppText.ACCESS_TOKEN));
httppost.setEntity(entity);
// Making server call
HttpResponse response = httpclient.execute(httppost);
HttpEntity r_entity = response.getEntity();
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
// Server response
statusCode = 200;
responseString = EntityUtils.toString(r_entity);
} else {
statusCode = 200;
responseString = "Error occurred! Http Status Code: " + statusCode;
}
} catch (ClientProtocolException e) {
responseString = e.toString();
} catch (IOException e) {
responseString = e.toString();
}
return responseString;
}
#Override
protected void onPostExecute(String result) {
hideProgressDialog();
}
}
5.Calling Async
new registerCall().execute();
Just replace getByteData() method with this below code:
private dataPartList = ArrayList();
#Override
protected Map<String, List<DataPart>> getByteDataList(){
Map<String, List<DataPart>> params = new HashMap<>();
params.put("yourKey","dataPartList");
return params
}
Has anyone been able to accomplish sending a multipart/form-data POST in Android with Volley yet? I have had no success trying to upload an image/png using a POST request to our server and am curious if anyone has.
I believe the default way to do this would be to override public byte[] getPostBody() in the Request.java class and attach the File there with a blank Header key for the boundary. However, converting my file to a String for the Map<String, String> postParams and then having it encoded again seems obtuse and not really elegant. Also I've been unsuccessful in my attempts. This is really the only thing holding us back from switching to this library.
Anyway, all thoughts and answers are extremely appreciated. Thank you for your help.
I might be wrong on this but I think you need to implement your own com.android.volley.toolbox.HttpStack for this because the default ones (HurlStack if version > Gingerbread or HttpClientStack) don't deal with multipart/form-data.
Edit:
And indeed I was wrong. I was able to do it using MultipartEntity in Request like this:
public class MultipartRequest extends Request<String> {
private MultipartEntity entity = new MultipartEntity();
private static final String FILE_PART_NAME = "file";
private static final String STRING_PART_NAME = "text";
private final Response.Listener<String> mListener;
private final File mFilePart;
private final String mStringPart;
public MultipartRequest(String url, Response.ErrorListener errorListener, Response.Listener<String> listener, File file, String stringPart)
{
super(Method.POST, url, errorListener);
mListener = listener;
mFilePart = file;
mStringPart = stringPart;
buildMultipartEntity();
}
private void buildMultipartEntity()
{
entity.addPart(FILE_PART_NAME, new FileBody(mFilePart));
try
{
entity.addPart(STRING_PART_NAME, new StringBody(mStringPart));
}
catch (UnsupportedEncodingException e)
{
VolleyLog.e("UnsupportedEncodingException");
}
}
#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);
}
}
It's pretty raw but I tried it with an image and a simple string and it works. The response is a placeholder, doesn't make much sense to return a Response String in this case. I had problems using apache httpmime to use MultipartEntity so I used this https://code.google.com/p/httpclientandroidlib/ don't know if there's a better way.
Hope it helps.
Edit
You can use httpmime without using httpclientandroidlib, the only dependency is httpcore.
As mentioned in the presentation at the I/O (about 4:05), Volley "is terrible" for large payloads. As I understand it that means not to use Volley for receiving/sending (big) files. Looking at the code it seems that it is not even designed to handle multipart form data (e.g. Request.java has getBodyContentType() with hardcoded "application/x-www-form-urlencoded"; HttpClientStack::createHttpRequest() can handle only byte[], etc...). Probably you will be able to create implementation that can handle multipart but If I were you I will just use HttpClient directly with MultipartEntity like:
HttpPost req = new HttpPost(composeTargetUrl());
MultipartEntity entity = new MultipartEntity();
entity.addPart(POST_IMAGE_VAR_NAME, new FileBody(toUpload));
try {
entity.addPart(POST_SESSION_VAR_NAME, new StringBody(uploadSessionId));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
req.setEntity(entity);
You may need newer HttpClient (i.e. not the built-in) or even better, use Volley with newer HttpClient
UPDATE 2015/08/26:
If you don't want to use deprecated HttpEntity, here is my working sample code (tested with ASP.Net WebAPI)
MultipartActivity.java
package com.example.volleyapp;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.view.Menu;
import android.view.MenuItem;
import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.example.volleyapp.BaseVolleyRequest;
import com.example.volleyapp.VolleySingleton;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class MultipartActivity extends Activity {
final Context mContext = this;
String mimeType;
DataOutputStream dos = null;
String lineEnd = "\r\n";
String boundary = "apiclient-" + System.currentTimeMillis();
String twoHyphens = "--";
int bytesRead, bytesAvailable, bufferSize;
byte[] buffer;
int maxBufferSize = 1024 * 1024;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_multipart);
Drawable drawable = ContextCompat.getDrawable(mContext, R.drawable.ic_action_file_attachment_light);
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
final byte[] bitmapData = byteArrayOutputStream.toByteArray();
String url = "http://192.168.1.100/api/postfile";
mimeType = "multipart/form-data;boundary=" + boundary;
BaseVolleyRequest baseVolleyRequest = new BaseVolleyRequest(1, url, new Response.Listener<NetworkResponse>() {
#Override
public void onResponse(NetworkResponse response) {
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
#Override
public String getBodyContentType() {
return mimeType;
}
#Override
public byte[] getBody() throws AuthFailureError {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
dos = new DataOutputStream(bos);
try {
dos.writeBytes(twoHyphens + boundary + lineEnd);
dos.writeBytes("Content-Disposition: form-data; name=\"uploaded_file\";filename=\""
+ "ic_action_file_attachment_light.png" + "\"" + lineEnd);
dos.writeBytes(lineEnd);
ByteArrayInputStream fileInputStream = new ByteArrayInputStream(bitmapData);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
// read file and write it into form...
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
while (bytesRead > 0) {
dos.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
// send multipart form data necesssary after file data...
dos.writeBytes(lineEnd);
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
return bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return bitmapData;
}
};
VolleySingleton.getInstance(mContext).addToRequestQueue(baseVolleyRequest);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_multipart, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
BaseVolleyRequest.java:
package com.example.volleyapp;
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 com.google.gson.JsonSyntaxException;
public class BaseVolleyRequest extends Request<NetworkResponse> {
private final Response.Listener<NetworkResponse> mListener;
private final Response.ErrorListener mErrorListener;
public BaseVolleyRequest(String url, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) {
super(0, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
}
public BaseVolleyRequest(int method, String url, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) {
super(method, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
}
#Override
protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) {
try {
return Response.success(
response,
HttpHeaderParser.parseCacheHeaders(response));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
} catch (Exception e) {
return Response.error(new ParseError(e));
}
}
#Override
protected void deliverResponse(NetworkResponse response) {
mListener.onResponse(response);
}
#Override
protected VolleyError parseNetworkError(VolleyError volleyError) {
return super.parseNetworkError(volleyError);
}
#Override
public void deliverError(VolleyError error) {
mErrorListener.onErrorResponse(error);
}
}
END OF UPDATE
This is my working sample code (only tested with small-size files):
public class FileUploadActivity extends Activity {
private final Context mContext = this;
HttpEntity httpEntity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file_upload);
Drawable drawable = getResources().getDrawable(R.drawable.ic_action_home);
if (drawable != null) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
final byte[] bitmapdata = stream.toByteArray();
String url = "http://10.0.2.2/api/fileupload";
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
// Add binary body
if (bitmapdata != null) {
ContentType contentType = ContentType.create("image/png");
String fileName = "ic_action_home.png";
builder.addBinaryBody("file", bitmapdata, contentType, fileName);
httpEntity = builder.build();
MyRequest myRequest = new MyRequest(Request.Method.POST, url, new Response.Listener<NetworkResponse>() {
#Override
public void onResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
Toast.makeText(mContext, jsonString, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(mContext, error.toString(), Toast.LENGTH_SHORT).show();
}
}) {
#Override
public String getBodyContentType() {
return httpEntity.getContentType().getValue();
}
#Override
public byte[] getBody() throws AuthFailureError {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
httpEntity.writeTo(bos);
} catch (IOException e) {
VolleyLog.e("IOException writing to ByteArrayOutputStream");
}
return bos.toByteArray();
}
};
MySingleton.getInstance(this).addToRequestQueue(myRequest);
}
}
}
...
}
public class MyRequest extends Request<NetworkResponse>
Complete Multipart Request with Upload Progress
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.util.CharsetUtils;
import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyLog;
import com.beusoft.app.AppContext;
public class MultipartRequest extends Request<String> {
MultipartEntityBuilder entity = MultipartEntityBuilder.create();
HttpEntity httpentity;
private String FILE_PART_NAME = "files";
private final Response.Listener<String> mListener;
private final File mFilePart;
private final Map<String, String> mStringPart;
private Map<String, String> headerParams;
private final MultipartProgressListener multipartProgressListener;
private long fileLength = 0L;
public MultipartRequest(String url, Response.ErrorListener errorListener,
Response.Listener<String> listener, File file, long fileLength,
Map<String, String> mStringPart,
final Map<String, String> headerParams, String partName,
MultipartProgressListener progLitener) {
super(Method.POST, url, errorListener);
this.mListener = listener;
this.mFilePart = file;
this.fileLength = fileLength;
this.mStringPart = mStringPart;
this.headerParams = headerParams;
this.FILE_PART_NAME = partName;
this.multipartProgressListener = progLitener;
entity.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
try {
entity.setCharset(CharsetUtils.get("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
buildMultipartEntity();
httpentity = entity.build();
}
// public void addStringBody(String param, String value) {
// if (mStringPart != null) {
// mStringPart.put(param, value);
// }
// }
private void buildMultipartEntity() {
entity.addPart(FILE_PART_NAME, new FileBody(mFilePart, ContentType.create("image/gif"), mFilePart.getName()));
if (mStringPart != null) {
for (Map.Entry<String, String> entry : mStringPart.entrySet()) {
entity.addTextBody(entry.getKey(), entry.getValue());
}
}
}
#Override
public String getBodyContentType() {
return httpentity.getContentType().getValue();
}
#Override
public byte[] getBody() throws AuthFailureError {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
httpentity.writeTo(new CountingOutputStream(bos, fileLength,
multipartProgressListener));
} catch (IOException e) {
VolleyLog.e("IOException writing to ByteArrayOutputStream");
}
return bos.toByteArray();
}
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
try {
// System.out.println("Network Response "+ new String(response.data, "UTF-8"));
return Response.success(new String(response.data, "UTF-8"),
getCacheEntry());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
// fuck it, it should never happen though
return Response.success(new String(response.data), getCacheEntry());
}
}
#Override
protected void deliverResponse(String response) {
mListener.onResponse(response);
}
//Override getHeaders() if you want to put anything in header
public static interface MultipartProgressListener {
void transferred(long transfered, int progress);
}
public static class CountingOutputStream extends FilterOutputStream {
private final MultipartProgressListener progListener;
private long transferred;
private long fileLength;
public CountingOutputStream(final OutputStream out, long fileLength,
final MultipartProgressListener listener) {
super(out);
this.fileLength = fileLength;
this.progListener = listener;
this.transferred = 0;
}
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
if (progListener != null) {
this.transferred += len;
int prog = (int) (transferred * 100 / fileLength);
this.progListener.transferred(this.transferred, prog);
}
}
public void write(int b) throws IOException {
out.write(b);
if (progListener != null) {
this.transferred++;
int prog = (int) (transferred * 100 / fileLength);
this.progListener.transferred(this.transferred, prog);
}
}
}
}
Sample Usage
protected <T> void uploadFile(final String tag, final String url,
final File file, final String partName,
final Map<String, String> headerParams,
final Response.Listener<String> resultDelivery,
final Response.ErrorListener errorListener,
MultipartProgressListener progListener) {
AZNetworkRetryPolicy retryPolicy = new AZNetworkRetryPolicy();
MultipartRequest mr = new MultipartRequest(url, errorListener,
resultDelivery, file, file.length(), null, headerParams,
partName, progListener);
mr.setRetryPolicy(retryPolicy);
mr.setTag(tag);
Volley.newRequestQueue(this).add(mr);
}
A very simple approach for the dev who just want to send POST parameters in multipart request.
Make the following changes in class which extends Request.java
First define these constants :
String BOUNDARY = "s2retfgsGSRFsERFGHfgdfgw734yhFHW567TYHSrf4yarg"; //This the boundary which is used by the server to split the post parameters.
String MULTIPART_FORMDATA = "multipart/form-data;boundary=" + BOUNDARY;
Add a helper function to create a post body for you :
private String createPostBody(Map<String, String> params) {
StringBuilder sbPost = new StringBuilder();
if (params != null) {
for (String key : params.keySet()) {
if (params.get(key) != null) {
sbPost.append("\r\n" + "--" + BOUNDARY + "\r\n");
sbPost.append("Content-Disposition: form-data; name=\"" + key + "\"" + "\r\n\r\n");
sbPost.append(params.get(key).toString());
}
}
}
return sbPost.toString();
}
Override getBody() and getBodyContentType
public String getBodyContentType() {
return MULTIPART_FORMDATA;
}
public byte[] getBody() throws AuthFailureError {
return createPostBody(getParams()).getBytes();
}
First answer on SO.
I have encountered the same problem and found #alex 's code very helpful. I have made some simple modifications in order to pass in as many parameters as needed through HashMap, and have basically copied parseNetworkResponse() from StringRequest. I have searched online and so surprised to find out that such a common task is so rarely answered. Anyway, I wish the code could help:
public class MultipartRequest extends Request<String> {
private MultipartEntity entity = new MultipartEntity();
private static final String FILE_PART_NAME = "image";
private final Response.Listener<String> mListener;
private final File file;
private final HashMap<String, String> params;
public MultipartRequest(String url, Response.Listener<String> listener, Response.ErrorListener errorListener, File file, HashMap<String, String> params)
{
super(Method.POST, url, errorListener);
mListener = listener;
this.file = file;
this.params = params;
buildMultipartEntity();
}
private void buildMultipartEntity()
{
entity.addPart(FILE_PART_NAME, new FileBody(file));
try
{
for ( String key : params.keySet() ) {
entity.addPart(key, new StringBody(params.get(key)));
}
}
catch (UnsupportedEncodingException e)
{
VolleyLog.e("UnsupportedEncodingException");
}
}
#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();
}
/**
* copied from Android StringRequest class
*/
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
#Override
protected void deliverResponse(String response)
{
mListener.onResponse(response);
}
And you may use the class as following:
HashMap<String, String> params = new HashMap<String, String>();
params.put("type", "Some Param");
params.put("location", "Some Param");
params.put("contact", "Some Param");
MultipartRequest mr = new MultipartRequest(url, new Response.Listener<String>(){
#Override
public void onResponse(String response) {
Log.d("response", response);
}
}, new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError error) {
Log.e("Volley Request Error", error.getLocalizedMessage());
}
}, f, params);
Volley.newRequestQueue(this).add(mr);
Another solution, very light with high performance with payload large:
Android Asynchronous Http Client library:
http://loopj.com/android-async-http/
private static AsyncHttpClient client = new AsyncHttpClient();
private void uploadFileExecute(File file) {
RequestParams params = new RequestParams();
try { params.put("photo", file); } catch (FileNotFoundException e) {}
client.post(getUrl(), params,
new AsyncHttpResponseHandler() {
public void onSuccess(String result) {
Log.d(TAG,"uploadFile response: "+result);
};
public void onFailure(Throwable arg0, String errorMsg) {
Log.d(TAG,"uploadFile ERROR!");
};
}
);
}
Here is Simple Solution And Complete Example for Uploading File Using Volley Android
1) Gradle Import
compile 'dev.dworks.libs:volleyplus:+'
2)Now Create a Class RequestManager
public class RequestManager {
private static RequestManager mRequestManager;
/**
* Queue which Manages the Network Requests :-)
*/
private static RequestQueue mRequestQueue;
// ImageLoader Instance
private RequestManager() {
}
public static RequestManager get(Context context) {
if (mRequestManager == null)
mRequestManager = new RequestManager();
return mRequestManager;
}
/**
* #param context application context
*/
public static RequestQueue getnstance(Context context) {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(context);
}
return mRequestQueue;
}
}
3)Now Create a Class to handle Request for uploading File WebService
public class WebService {
private RequestQueue mRequestQueue;
private static WebService apiRequests = null;
public static WebService getInstance() {
if (apiRequests == null) {
apiRequests = new WebService();
return apiRequests;
}
return apiRequests;
}
public void updateProfile(Context context, String doc_name, String doc_type, String appliance_id, File file, Response.Listener<String> listener, Response.ErrorListener errorListener) {
SimpleMultiPartRequest request = new SimpleMultiPartRequest(Request.Method.POST, "YOUR URL HERE", listener, errorListener);
// request.setParams(data);
mRequestQueue = RequestManager.getnstance(context);
request.addMultipartParam("token", "text", "tdfysghfhsdfh");
request.addMultipartParam("parameter_1", "text", doc_name);
request.addMultipartParam("dparameter_2", "text", doc_type);
request.addMultipartParam("parameter_3", "text", appliance_id);
request.addFile("document_file", file.getPath());
request.setFixedStreamingMode(true);
mRequestQueue.add(request);
}
}
4) And Now Call The method Like This to Hit the service
public class Main2Activity extends AppCompatActivity implements Response.ErrorListener, Response.Listener<String>{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Button button=(Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
uploadData();
}
});
}
private void uploadData() {
WebService.getInstance().updateProfile(getActivity(), "appl_doc", "appliance", "1", mChoosenFile, this, this);
}
#Override
public void onErrorResponse(VolleyError error) {
}
#Override
public void onResponse(String response) {
//Your response here
}
}
This is my way of doing it. It may be useful to others :
private void updateType(){
// Log.i(TAG,"updateType");
StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
// running on main thread-------
try {
JSONObject res = new JSONObject(response);
res.getString("result");
System.out.println("Response:" + res.getString("result"));
}else{
CustomTast ct=new CustomTast(context);
ct.showCustomAlert("Network/Server Disconnected",R.drawable.disconnect);
}
} catch (Exception e) {
e.printStackTrace();
//Log.e("Response", "==> " + e.getMessage());
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
// running on main thread-------
VolleyLog.d(TAG, "Error: " + volleyError.getMessage());
}
}) {
protected Map<String, String> getParams() {
HashMap<String, String> hashMapParams = new HashMap<String, String>();
hashMapParams.put("key", "value");
hashMapParams.put("key", "value");
hashMapParams.put("key", "value"));
hashMapParams.put("key", "value");
System.out.println("Hashmap:" + hashMapParams);
return hashMapParams;
}
};
AppController.getInstance().addToRequestQueue(request);
}
Has anyone been able to accomplish sending a multipart/form-data POST in Android with Volley yet? I have had no success trying to upload an image/png using a POST request to our server and am curious if anyone has.
I believe the default way to do this would be to override public byte[] getPostBody() in the Request.java class and attach the File there with a blank Header key for the boundary. However, converting my file to a String for the Map<String, String> postParams and then having it encoded again seems obtuse and not really elegant. Also I've been unsuccessful in my attempts. This is really the only thing holding us back from switching to this library.
Anyway, all thoughts and answers are extremely appreciated. Thank you for your help.
I might be wrong on this but I think you need to implement your own com.android.volley.toolbox.HttpStack for this because the default ones (HurlStack if version > Gingerbread or HttpClientStack) don't deal with multipart/form-data.
Edit:
And indeed I was wrong. I was able to do it using MultipartEntity in Request like this:
public class MultipartRequest extends Request<String> {
private MultipartEntity entity = new MultipartEntity();
private static final String FILE_PART_NAME = "file";
private static final String STRING_PART_NAME = "text";
private final Response.Listener<String> mListener;
private final File mFilePart;
private final String mStringPart;
public MultipartRequest(String url, Response.ErrorListener errorListener, Response.Listener<String> listener, File file, String stringPart)
{
super(Method.POST, url, errorListener);
mListener = listener;
mFilePart = file;
mStringPart = stringPart;
buildMultipartEntity();
}
private void buildMultipartEntity()
{
entity.addPart(FILE_PART_NAME, new FileBody(mFilePart));
try
{
entity.addPart(STRING_PART_NAME, new StringBody(mStringPart));
}
catch (UnsupportedEncodingException e)
{
VolleyLog.e("UnsupportedEncodingException");
}
}
#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);
}
}
It's pretty raw but I tried it with an image and a simple string and it works. The response is a placeholder, doesn't make much sense to return a Response String in this case. I had problems using apache httpmime to use MultipartEntity so I used this https://code.google.com/p/httpclientandroidlib/ don't know if there's a better way.
Hope it helps.
Edit
You can use httpmime without using httpclientandroidlib, the only dependency is httpcore.
As mentioned in the presentation at the I/O (about 4:05), Volley "is terrible" for large payloads. As I understand it that means not to use Volley for receiving/sending (big) files. Looking at the code it seems that it is not even designed to handle multipart form data (e.g. Request.java has getBodyContentType() with hardcoded "application/x-www-form-urlencoded"; HttpClientStack::createHttpRequest() can handle only byte[], etc...). Probably you will be able to create implementation that can handle multipart but If I were you I will just use HttpClient directly with MultipartEntity like:
HttpPost req = new HttpPost(composeTargetUrl());
MultipartEntity entity = new MultipartEntity();
entity.addPart(POST_IMAGE_VAR_NAME, new FileBody(toUpload));
try {
entity.addPart(POST_SESSION_VAR_NAME, new StringBody(uploadSessionId));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
req.setEntity(entity);
You may need newer HttpClient (i.e. not the built-in) or even better, use Volley with newer HttpClient
UPDATE 2015/08/26:
If you don't want to use deprecated HttpEntity, here is my working sample code (tested with ASP.Net WebAPI)
MultipartActivity.java
package com.example.volleyapp;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.view.Menu;
import android.view.MenuItem;
import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.example.volleyapp.BaseVolleyRequest;
import com.example.volleyapp.VolleySingleton;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class MultipartActivity extends Activity {
final Context mContext = this;
String mimeType;
DataOutputStream dos = null;
String lineEnd = "\r\n";
String boundary = "apiclient-" + System.currentTimeMillis();
String twoHyphens = "--";
int bytesRead, bytesAvailable, bufferSize;
byte[] buffer;
int maxBufferSize = 1024 * 1024;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_multipart);
Drawable drawable = ContextCompat.getDrawable(mContext, R.drawable.ic_action_file_attachment_light);
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
final byte[] bitmapData = byteArrayOutputStream.toByteArray();
String url = "http://192.168.1.100/api/postfile";
mimeType = "multipart/form-data;boundary=" + boundary;
BaseVolleyRequest baseVolleyRequest = new BaseVolleyRequest(1, url, new Response.Listener<NetworkResponse>() {
#Override
public void onResponse(NetworkResponse response) {
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
#Override
public String getBodyContentType() {
return mimeType;
}
#Override
public byte[] getBody() throws AuthFailureError {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
dos = new DataOutputStream(bos);
try {
dos.writeBytes(twoHyphens + boundary + lineEnd);
dos.writeBytes("Content-Disposition: form-data; name=\"uploaded_file\";filename=\""
+ "ic_action_file_attachment_light.png" + "\"" + lineEnd);
dos.writeBytes(lineEnd);
ByteArrayInputStream fileInputStream = new ByteArrayInputStream(bitmapData);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
// read file and write it into form...
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
while (bytesRead > 0) {
dos.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
// send multipart form data necesssary after file data...
dos.writeBytes(lineEnd);
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
return bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return bitmapData;
}
};
VolleySingleton.getInstance(mContext).addToRequestQueue(baseVolleyRequest);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_multipart, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
BaseVolleyRequest.java:
package com.example.volleyapp;
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 com.google.gson.JsonSyntaxException;
public class BaseVolleyRequest extends Request<NetworkResponse> {
private final Response.Listener<NetworkResponse> mListener;
private final Response.ErrorListener mErrorListener;
public BaseVolleyRequest(String url, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) {
super(0, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
}
public BaseVolleyRequest(int method, String url, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) {
super(method, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
}
#Override
protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) {
try {
return Response.success(
response,
HttpHeaderParser.parseCacheHeaders(response));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
} catch (Exception e) {
return Response.error(new ParseError(e));
}
}
#Override
protected void deliverResponse(NetworkResponse response) {
mListener.onResponse(response);
}
#Override
protected VolleyError parseNetworkError(VolleyError volleyError) {
return super.parseNetworkError(volleyError);
}
#Override
public void deliverError(VolleyError error) {
mErrorListener.onErrorResponse(error);
}
}
END OF UPDATE
This is my working sample code (only tested with small-size files):
public class FileUploadActivity extends Activity {
private final Context mContext = this;
HttpEntity httpEntity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file_upload);
Drawable drawable = getResources().getDrawable(R.drawable.ic_action_home);
if (drawable != null) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
final byte[] bitmapdata = stream.toByteArray();
String url = "http://10.0.2.2/api/fileupload";
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
// Add binary body
if (bitmapdata != null) {
ContentType contentType = ContentType.create("image/png");
String fileName = "ic_action_home.png";
builder.addBinaryBody("file", bitmapdata, contentType, fileName);
httpEntity = builder.build();
MyRequest myRequest = new MyRequest(Request.Method.POST, url, new Response.Listener<NetworkResponse>() {
#Override
public void onResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
Toast.makeText(mContext, jsonString, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(mContext, error.toString(), Toast.LENGTH_SHORT).show();
}
}) {
#Override
public String getBodyContentType() {
return httpEntity.getContentType().getValue();
}
#Override
public byte[] getBody() throws AuthFailureError {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
httpEntity.writeTo(bos);
} catch (IOException e) {
VolleyLog.e("IOException writing to ByteArrayOutputStream");
}
return bos.toByteArray();
}
};
MySingleton.getInstance(this).addToRequestQueue(myRequest);
}
}
}
...
}
public class MyRequest extends Request<NetworkResponse>
Complete Multipart Request with Upload Progress
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.util.CharsetUtils;
import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyLog;
import com.beusoft.app.AppContext;
public class MultipartRequest extends Request<String> {
MultipartEntityBuilder entity = MultipartEntityBuilder.create();
HttpEntity httpentity;
private String FILE_PART_NAME = "files";
private final Response.Listener<String> mListener;
private final File mFilePart;
private final Map<String, String> mStringPart;
private Map<String, String> headerParams;
private final MultipartProgressListener multipartProgressListener;
private long fileLength = 0L;
public MultipartRequest(String url, Response.ErrorListener errorListener,
Response.Listener<String> listener, File file, long fileLength,
Map<String, String> mStringPart,
final Map<String, String> headerParams, String partName,
MultipartProgressListener progLitener) {
super(Method.POST, url, errorListener);
this.mListener = listener;
this.mFilePart = file;
this.fileLength = fileLength;
this.mStringPart = mStringPart;
this.headerParams = headerParams;
this.FILE_PART_NAME = partName;
this.multipartProgressListener = progLitener;
entity.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
try {
entity.setCharset(CharsetUtils.get("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
buildMultipartEntity();
httpentity = entity.build();
}
// public void addStringBody(String param, String value) {
// if (mStringPart != null) {
// mStringPart.put(param, value);
// }
// }
private void buildMultipartEntity() {
entity.addPart(FILE_PART_NAME, new FileBody(mFilePart, ContentType.create("image/gif"), mFilePart.getName()));
if (mStringPart != null) {
for (Map.Entry<String, String> entry : mStringPart.entrySet()) {
entity.addTextBody(entry.getKey(), entry.getValue());
}
}
}
#Override
public String getBodyContentType() {
return httpentity.getContentType().getValue();
}
#Override
public byte[] getBody() throws AuthFailureError {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
httpentity.writeTo(new CountingOutputStream(bos, fileLength,
multipartProgressListener));
} catch (IOException e) {
VolleyLog.e("IOException writing to ByteArrayOutputStream");
}
return bos.toByteArray();
}
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
try {
// System.out.println("Network Response "+ new String(response.data, "UTF-8"));
return Response.success(new String(response.data, "UTF-8"),
getCacheEntry());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
// fuck it, it should never happen though
return Response.success(new String(response.data), getCacheEntry());
}
}
#Override
protected void deliverResponse(String response) {
mListener.onResponse(response);
}
//Override getHeaders() if you want to put anything in header
public static interface MultipartProgressListener {
void transferred(long transfered, int progress);
}
public static class CountingOutputStream extends FilterOutputStream {
private final MultipartProgressListener progListener;
private long transferred;
private long fileLength;
public CountingOutputStream(final OutputStream out, long fileLength,
final MultipartProgressListener listener) {
super(out);
this.fileLength = fileLength;
this.progListener = listener;
this.transferred = 0;
}
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
if (progListener != null) {
this.transferred += len;
int prog = (int) (transferred * 100 / fileLength);
this.progListener.transferred(this.transferred, prog);
}
}
public void write(int b) throws IOException {
out.write(b);
if (progListener != null) {
this.transferred++;
int prog = (int) (transferred * 100 / fileLength);
this.progListener.transferred(this.transferred, prog);
}
}
}
}
Sample Usage
protected <T> void uploadFile(final String tag, final String url,
final File file, final String partName,
final Map<String, String> headerParams,
final Response.Listener<String> resultDelivery,
final Response.ErrorListener errorListener,
MultipartProgressListener progListener) {
AZNetworkRetryPolicy retryPolicy = new AZNetworkRetryPolicy();
MultipartRequest mr = new MultipartRequest(url, errorListener,
resultDelivery, file, file.length(), null, headerParams,
partName, progListener);
mr.setRetryPolicy(retryPolicy);
mr.setTag(tag);
Volley.newRequestQueue(this).add(mr);
}
A very simple approach for the dev who just want to send POST parameters in multipart request.
Make the following changes in class which extends Request.java
First define these constants :
String BOUNDARY = "s2retfgsGSRFsERFGHfgdfgw734yhFHW567TYHSrf4yarg"; //This the boundary which is used by the server to split the post parameters.
String MULTIPART_FORMDATA = "multipart/form-data;boundary=" + BOUNDARY;
Add a helper function to create a post body for you :
private String createPostBody(Map<String, String> params) {
StringBuilder sbPost = new StringBuilder();
if (params != null) {
for (String key : params.keySet()) {
if (params.get(key) != null) {
sbPost.append("\r\n" + "--" + BOUNDARY + "\r\n");
sbPost.append("Content-Disposition: form-data; name=\"" + key + "\"" + "\r\n\r\n");
sbPost.append(params.get(key).toString());
}
}
}
return sbPost.toString();
}
Override getBody() and getBodyContentType
public String getBodyContentType() {
return MULTIPART_FORMDATA;
}
public byte[] getBody() throws AuthFailureError {
return createPostBody(getParams()).getBytes();
}
First answer on SO.
I have encountered the same problem and found #alex 's code very helpful. I have made some simple modifications in order to pass in as many parameters as needed through HashMap, and have basically copied parseNetworkResponse() from StringRequest. I have searched online and so surprised to find out that such a common task is so rarely answered. Anyway, I wish the code could help:
public class MultipartRequest extends Request<String> {
private MultipartEntity entity = new MultipartEntity();
private static final String FILE_PART_NAME = "image";
private final Response.Listener<String> mListener;
private final File file;
private final HashMap<String, String> params;
public MultipartRequest(String url, Response.Listener<String> listener, Response.ErrorListener errorListener, File file, HashMap<String, String> params)
{
super(Method.POST, url, errorListener);
mListener = listener;
this.file = file;
this.params = params;
buildMultipartEntity();
}
private void buildMultipartEntity()
{
entity.addPart(FILE_PART_NAME, new FileBody(file));
try
{
for ( String key : params.keySet() ) {
entity.addPart(key, new StringBody(params.get(key)));
}
}
catch (UnsupportedEncodingException e)
{
VolleyLog.e("UnsupportedEncodingException");
}
}
#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();
}
/**
* copied from Android StringRequest class
*/
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
#Override
protected void deliverResponse(String response)
{
mListener.onResponse(response);
}
And you may use the class as following:
HashMap<String, String> params = new HashMap<String, String>();
params.put("type", "Some Param");
params.put("location", "Some Param");
params.put("contact", "Some Param");
MultipartRequest mr = new MultipartRequest(url, new Response.Listener<String>(){
#Override
public void onResponse(String response) {
Log.d("response", response);
}
}, new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError error) {
Log.e("Volley Request Error", error.getLocalizedMessage());
}
}, f, params);
Volley.newRequestQueue(this).add(mr);
Another solution, very light with high performance with payload large:
Android Asynchronous Http Client library:
http://loopj.com/android-async-http/
private static AsyncHttpClient client = new AsyncHttpClient();
private void uploadFileExecute(File file) {
RequestParams params = new RequestParams();
try { params.put("photo", file); } catch (FileNotFoundException e) {}
client.post(getUrl(), params,
new AsyncHttpResponseHandler() {
public void onSuccess(String result) {
Log.d(TAG,"uploadFile response: "+result);
};
public void onFailure(Throwable arg0, String errorMsg) {
Log.d(TAG,"uploadFile ERROR!");
};
}
);
}
Here is Simple Solution And Complete Example for Uploading File Using Volley Android
1) Gradle Import
compile 'dev.dworks.libs:volleyplus:+'
2)Now Create a Class RequestManager
public class RequestManager {
private static RequestManager mRequestManager;
/**
* Queue which Manages the Network Requests :-)
*/
private static RequestQueue mRequestQueue;
// ImageLoader Instance
private RequestManager() {
}
public static RequestManager get(Context context) {
if (mRequestManager == null)
mRequestManager = new RequestManager();
return mRequestManager;
}
/**
* #param context application context
*/
public static RequestQueue getnstance(Context context) {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(context);
}
return mRequestQueue;
}
}
3)Now Create a Class to handle Request for uploading File WebService
public class WebService {
private RequestQueue mRequestQueue;
private static WebService apiRequests = null;
public static WebService getInstance() {
if (apiRequests == null) {
apiRequests = new WebService();
return apiRequests;
}
return apiRequests;
}
public void updateProfile(Context context, String doc_name, String doc_type, String appliance_id, File file, Response.Listener<String> listener, Response.ErrorListener errorListener) {
SimpleMultiPartRequest request = new SimpleMultiPartRequest(Request.Method.POST, "YOUR URL HERE", listener, errorListener);
// request.setParams(data);
mRequestQueue = RequestManager.getnstance(context);
request.addMultipartParam("token", "text", "tdfysghfhsdfh");
request.addMultipartParam("parameter_1", "text", doc_name);
request.addMultipartParam("dparameter_2", "text", doc_type);
request.addMultipartParam("parameter_3", "text", appliance_id);
request.addFile("document_file", file.getPath());
request.setFixedStreamingMode(true);
mRequestQueue.add(request);
}
}
4) And Now Call The method Like This to Hit the service
public class Main2Activity extends AppCompatActivity implements Response.ErrorListener, Response.Listener<String>{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Button button=(Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
uploadData();
}
});
}
private void uploadData() {
WebService.getInstance().updateProfile(getActivity(), "appl_doc", "appliance", "1", mChoosenFile, this, this);
}
#Override
public void onErrorResponse(VolleyError error) {
}
#Override
public void onResponse(String response) {
//Your response here
}
}
This is my way of doing it. It may be useful to others :
private void updateType(){
// Log.i(TAG,"updateType");
StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
// running on main thread-------
try {
JSONObject res = new JSONObject(response);
res.getString("result");
System.out.println("Response:" + res.getString("result"));
}else{
CustomTast ct=new CustomTast(context);
ct.showCustomAlert("Network/Server Disconnected",R.drawable.disconnect);
}
} catch (Exception e) {
e.printStackTrace();
//Log.e("Response", "==> " + e.getMessage());
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
// running on main thread-------
VolleyLog.d(TAG, "Error: " + volleyError.getMessage());
}
}) {
protected Map<String, String> getParams() {
HashMap<String, String> hashMapParams = new HashMap<String, String>();
hashMapParams.put("key", "value");
hashMapParams.put("key", "value");
hashMapParams.put("key", "value"));
hashMapParams.put("key", "value");
System.out.println("Hashmap:" + hashMapParams);
return hashMapParams;
}
};
AppController.getInstance().addToRequestQueue(request);
}