Search code examples
androidandroid-volleyhttpentity

How to send multipart request using Volley without HttpEntity?


I am following the solution to send multi-part request using volley from How to multipart data using Android Volley. But, since SDK 22, httpentity is deprecated and it's removed completely on SDK 23.

The solution is to use openConnection, just like HttpEntity is deprecated on Android now, what's the alternative?, but I don't know how to use it for multi-part request


Solution

  • I have found a solution for this (based on Multipart/form-data requests in Android: HttpURLConnection vs OkHttp )

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