Created
February 10, 2018 16:44
-
-
Save harshvishu/cd0ebd5f89aef0f5c5ef2ce06a4322da to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.com; | |
import com.android.volley.AuthFailureError; | |
import com.android.volley.NetworkResponse; | |
import com.android.volley.ParseError; | |
import com.android.volley.Request; | |
import com.android.volley.Response; | |
import com.android.volley.VolleyError; | |
import com.android.volley.toolbox.HttpHeaderParser; | |
import java.io.ByteArrayInputStream; | |
import java.io.ByteArrayOutputStream; | |
import java.io.DataOutputStream; | |
import java.io.IOException; | |
import java.io.UnsupportedEncodingException; | |
import java.util.Map; | |
/** | |
* Custom request to make postMultipart header and upload file. | |
*/ | |
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 apiError 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 url request destination | |
* @param listener on success event handler | |
* @param errorListener on apiError event handler | |
*/ | |
public VolleyMultipartRequest(String url, | |
Response.Listener<NetworkResponse> listener, | |
Response.ErrorListener errorListener) { | |
super(Request.Method.POST, 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 "postMultipart/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 postMultipart 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 static 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; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment