How to send a “multipart/form-data” POST in Android with Volley

后端 未结 9 2184
不思量自难忘°
不思量自难忘° 2020-11-22 03:50

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

相关标签:
9条回答
  • 2020-11-22 04:18

    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);
    
    0 讨论(0)
  • 2020-11-22 04:19

    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 
        }
    }
    
    0 讨论(0)
  • 2020-11-22 04:26

    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();
    }
    
    0 讨论(0)
  • 2020-11-22 04:29

    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!");
    
                };
    
            }
    
        );
    
    }
    
    0 讨论(0)
  • 2020-11-22 04:31

    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.

    0 讨论(0)
  • 2020-11-22 04:33

    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

    0 讨论(0)
提交回复
热议问题