This is not really a question, however, I would like to share some of my working code here for your reference when you need.
As we know that HttpEntity
Just want to add to the answer. I was trying to figure how to append text fields to the body and created the following function to do it:
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);
}
It is working pretty well.
I found a wrapper of the original volley library which is easier to integrate for multi-part requests. It also supports uploading the multi-part data along with other request parameters. Hence I am sharing my code for the future developers who might run into the problem that I was having (i.e. uploading multi-part data using volley along with some other parameters).
Add the following library in the build.gradle
file.
dependencies {
compile 'dev.dworks.libs:volleyplus:+'
}
Please note that, I removed the original volley library from my build.gradle
and used the above library instead which can handle both multi-part and normal requests having similar integration technique.
Then I just had to write the following class which handles the POST request operation.
public class POSTMediasTask {
public void uploadMedia(final Context context, String filePath) {
String url = getUrlForPOSTMedia(); // This is a dummy function which returns the POST url for you
SimpleMultiPartRequest multiPartRequestWithParams = new SimpleMultiPartRequest(Request.Method.POST, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("Response", response);
// TODO: Do something on success
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// TODO: Handle your error here
}
});
// Add the file here
multiPartRequestWithParams.addFile("file", filePath);
// Add the params here
multiPartRequestWithParams.addStringParam("param1", "SomeParamValue1");
multiPartRequestWithParams.addStringParam("param2", "SomeParamValue2");
RequestQueue queue = Volley.newRequestQueue(context);
queue.add(multiPartRequestWithParams);
}
}
Now execute the task like the following.
new POSTMediasTask().uploadMedia(context, mediaPath);
You can upload one file at a time using this library. However, I could manage to upload multiple files, just by initiating multiple tasks.
Hope that helps!
I rewrite your code @RacZo and @BNK more modular and easy to use like
VolleyMultipartRequest multipartRequest = new VolleyMultipartRequest(Request.Method.POST, url, new Response.Listener<NetworkResponse>() {
@Override
public void onResponse(NetworkResponse response) {
String resultResponse = new String(response.data);
// parse success output
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("api_token", "gh659gjhvdyudo973823tt9gvjf7i6ric75r76");
params.put("name", "Angga");
params.put("location", "Indonesia");
params.put("about", "UI/UX Designer");
params.put("contact", "angga@email.com");
return params;
}
@Override
protected Map<String, DataPart> getByteData() {
Map<String, DataPart> params = new HashMap<>();
// file name could found file base or direct access from real path
// for now just get bitmap data from ImageView
params.put("avatar", new DataPart("file_avatar.jpg", AppHelper.getFileDataFromDrawable(getBaseContext(), mAvatarImage.getDrawable()), "image/jpeg"));
params.put("cover", new DataPart("file_cover.jpg", AppHelper.getFileDataFromDrawable(getBaseContext(), mCoverImage.getDrawable()), "image/jpeg"));
return params;
}
};
VolleySingleton.getInstance(getBaseContext()).addToRequestQueue(multipartRequest);
Check full of code VolleyMultipartRequest
at my gist.
For those who are struggling to send utf-8 parameters and still no luck, the problem I had was in the dataOutputStream, and change the code of @RacZo to below code:
private void buildTextPart(DataOutputStream dataOutputStream, String parameterName, String parameterValue) throws IOException {
dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"");
dataOutputStream.write(parameterName.getBytes("UTF-8"));
dataOutputStream.writeBytes(lineEnd);
dataOutputStream.writeBytes("Content-Type: text/plain; charset=UTF-8" + lineEnd);
dataOutputStream.writeBytes(lineEnd);
dataOutputStream.write(parameterValue.getBytes("UTF-8"));
dataOutputStream.writeBytes(lineEnd);
}
Here is a Kotlin version of a class allowing multipart request with Volley 1.1.1.
It's mostly based on @BNK's solution but slighly simplified. I did not notice any particular performance issue. I uploaded a 5Mb pic in about 3 seconds.
class MultipartWebservice(context: Context) {
private var queue: RequestQueue? = null
private val boundary = "apiclient-" + System.currentTimeMillis()
private val mimeType = "multipart/form-data;boundary=$boundary"
init {
queue = Volley.newRequestQueue(context)
}
fun sendMultipartRequest(
method: Int,
url: String,
fileData: ByteArray,
fileName: String,
listener: Response.Listener<NetworkResponse>,
errorListener: Response.ErrorListener
) {
// Create multi part byte array
val bos = ByteArrayOutputStream()
val dos = DataOutputStream(bos)
buildMultipartContent(dos, fileData, fileName)
val multipartBody = bos.toByteArray()
// Request header, if needed
val headers = HashMap<String, String>()
headers["API-TOKEN"] = "458e126682d577c97d225bbd73a75b5989f65e977b6d8d4b2267537019ad9d20"
val request = MultipartRequest(
method,
url,
errorListener,
listener,
headers,
mimeType,
multipartBody
)
queue?.add(request)
}
@Throws(IOException::class)
private fun buildMultipartContent(dos: DataOutputStream, fileData: ByteArray, fileName: String) {
val twoHyphens = "--"
val lineEnd = "\r\n"
dos.writeBytes(twoHyphens + boundary + lineEnd)
dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"$fileName\"$lineEnd")
dos.writeBytes(lineEnd)
dos.write(fileData)
dos.writeBytes(lineEnd)
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd)
}
class MultipartRequest(
method: Int,
url: String,
errorListener: Response.ErrorListener?,
private var listener: Response.Listener<NetworkResponse>,
private var headers: MutableMap<String, String>,
private var mimeType: String,
private var multipartBody: ByteArray
) : Request<NetworkResponse>(method, url, errorListener) {
override fun getHeaders(): MutableMap<String, String> {
return if (headers.isEmpty()) super.getHeaders() else headers
}
override fun getBodyContentType(): String {
return mimeType
}
override fun getBody(): ByteArray {
return multipartBody
}
override fun parseNetworkResponse(response: NetworkResponse?): Response<NetworkResponse> {
return try {
Response.success(response, HttpHeaderParser.parseCacheHeaders(response))
} catch (e: Exception) {
Response.error(ParseError(e))
}
}
override fun deliverResponse(response: NetworkResponse?) {
listener.onResponse(response)
}
}
}