Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save AizazZaidee/55c316152ecace931bc0 to your computer and use it in GitHub Desktop.
Save AizazZaidee/55c316152ecace931bc0 to your computer and use it in GitHub Desktop.
Android Volley 2015-01-15 Multipart Request with Upload Progress
package com.az.custom.request;
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
public Map<String, String> getHeaders() throws AuthFailureError {
if (headerParams == null) {
headerParams = new HashMap<String, String>(1);
if (AppContext.userPojo != null) {
headerParams.put("sid", AppContext.userPojo.SID);
}
return headerParams;
} else {
if (AppContext.userPojo != null) {
headerParams.put("sid", AppContext.userPojo.SID);
}
}
return headerParams;
}
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);
}
}
}
}
@rambabusaravanan
Copy link

rambabusaravanan commented Jul 26, 2016

The transferred() function in the listener is called very fast up to 100% in seconds, but the media upload takes minutes ..
Can you help us please ..

@liorzam
Copy link

liorzam commented Sep 5, 2016

Hey i delt with sending multipart request with volley and in your case you didnt solve a serious problem
if you take a quick look of this code , The base getBody of volley request expect to return byte[] which mean that you will load all your content to the memory instead of loading chunks of data and write it on the connection's output stream

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(); }

to solve this issue you have to provide your own HurlStack to volly use the writeTo of your multipartEntity which will write to the outputstream

` private static void addMultipartBodyIfExists(HttpURLConnection connection, MultipartRequest<?> request)
throws IOException, AuthFailureError {
connection.setDoOutput(true);

    HttpEntity entity = request.getMultiBody();
    try {
        connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
        entity.writeTo(connection.getOutputStream());
    } catch (IOException ioe) {
    }

// The original code of volly
// byte[] body = request.getBody();
// if (body != null) {
// connection.setDoOutput(true);
// connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
// DataOutputStream out = new DataOutputStream(connection.getOutputStream());
// out.write(body);
// out.close();
// }
}`

@CapnSpellcheck
Copy link

CapnSpellcheck commented Mar 23, 2017

@liorzam so what's the full code of the HurlStack? Did you extend HurlStack and override, or implement HttpStack and copy all the code?

@CapnSpellcheck
Copy link

And getCacheEntry()...? Any reason it's not HttpHeaderParser.parseCacheHeaders?

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