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

后端 未结 9 2175
不思量自难忘°
不思量自难忘° 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:33

    UPDATE 2015/08/26:

    If you don't want to use deprecated HttpEntity, 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);
        }
    }
    

    END OF UPDATE

    This is my working sample code (only tested with small-size files):

    public class FileUploadActivity extends Activity {
    
        private final Context mContext = this;
        HttpEntity httpEntity;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_file_upload);   
    
            Drawable drawable = getResources().getDrawable(R.drawable.ic_action_home);
            if (drawable != null) {
                Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
                final byte[] bitmapdata = stream.toByteArray();
                String url = "http://10.0.2.2/api/fileupload";
                MultipartEntityBuilder builder = MultipartEntityBuilder.create();
                builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
    
                // Add binary body
                if (bitmapdata != null) {
                    ContentType contentType = ContentType.create("image/png");
                    String fileName = "ic_action_home.png";
                    builder.addBinaryBody("file", bitmapdata, contentType, fileName);
                    httpEntity = builder.build();
    
                    MyRequest myRequest = new MyRequest(Request.Method.POST, url, new Response.Listener<NetworkResponse>() {
                        @Override
                        public void onResponse(NetworkResponse response) {
                            try {                            
                                String jsonString = new String(response.data,
                                        HttpHeaderParser.parseCharset(response.headers));
                                Toast.makeText(mContext, jsonString, Toast.LENGTH_SHORT).show();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }, new Response.ErrorListener() {
                        @Override
                        public void onErrorResponse(VolleyError error) {
                            Toast.makeText(mContext, error.toString(), Toast.LENGTH_SHORT).show();                        
                        }
                    }) {
                        @Override
                        public String getBodyContentType() {
                            return httpEntity.getContentType().getValue();
                        }
    
                        @Override
                        public byte[] getBody() throws AuthFailureError {
                            ByteArrayOutputStream bos = new ByteArrayOutputStream();
                            try {
                                httpEntity.writeTo(bos);
                            } catch (IOException e) {
                                VolleyLog.e("IOException writing to ByteArrayOutputStream");
                            }
                            return bos.toByteArray();
                        }
                    };
    
                    MySingleton.getInstance(this).addToRequestQueue(myRequest);
                }
            }
        }
    
        ...
    }
    
    public class MyRequest extends Request<NetworkResponse>
    
    0 讨论(0)
  • 2020-11-22 04:33

    Complete Multipart Request with Upload Progress

    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 getHeaders() if you want to put anything in header
    
        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);
                }
            }
    
        }
    }
    

    Sample Usage

    protected <T> void uploadFile(final String tag, final String url,
                final File file, final String partName,         
                final Map<String, String> headerParams,
                final Response.Listener<String> resultDelivery,
                final Response.ErrorListener errorListener,
                MultipartProgressListener progListener) {
            AZNetworkRetryPolicy retryPolicy = new AZNetworkRetryPolicy();
    
            MultipartRequest mr = new MultipartRequest(url, errorListener,
                    resultDelivery, file, file.length(), null, headerParams,
                    partName, progListener);
    
            mr.setRetryPolicy(retryPolicy);
            mr.setTag(tag);
    
            Volley.newRequestQueue(this).add(mr);
    
        }
    
    0 讨论(0)
  • 2020-11-22 04:38

    This is my way of doing it. It may be useful to others :

    private void updateType(){
        // Log.i(TAG,"updateType");
         StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
    
             @Override
             public void onResponse(String response) {
                 // running on main thread-------
                 try {
                     JSONObject res = new JSONObject(response);
                     res.getString("result");
                     System.out.println("Response:" + res.getString("result"));
    
                     }else{
                         CustomTast ct=new CustomTast(context);
                         ct.showCustomAlert("Network/Server Disconnected",R.drawable.disconnect);
                     }
    
                 } catch (Exception e) {
                     e.printStackTrace();
    
                     //Log.e("Response", "==> " + e.getMessage());
                 }
             }
         }, new Response.ErrorListener() {
             @Override
             public void onErrorResponse(VolleyError volleyError) {
                 // running on main thread-------
                 VolleyLog.d(TAG, "Error: " + volleyError.getMessage());
    
             }
         }) {
             protected Map<String, String> getParams() {
                 HashMap<String, String> hashMapParams = new HashMap<String, String>();
                 hashMapParams.put("key", "value");
                 hashMapParams.put("key", "value");
                 hashMapParams.put("key", "value"));
                 hashMapParams.put("key", "value");
                 System.out.println("Hashmap:" + hashMapParams);
                 return hashMapParams;
             }
         };
         AppController.getInstance().addToRequestQueue(request);
    
     }
    
    0 讨论(0)
提交回复
热议问题