Search code examples
androidjsonfile-uploadmultipartform-datamultipart

Android multipart image upload with HttpConnectionParams deprecated in new api


I am using MultiPartRequester class for multipart image upload to server, but i found that some part is deprecated in this. for example HttpConnectionParams , getConnectionManager() etc. so anyone have new solution that is not deprecated with new API level for file upload?

I am using this code.

public class MultiPartRequester {
	private Map<String, String> map;
	private AsyncTaskCompleteListener mAsynclistener;
	private int serviceCode;
	private HttpClient httpclient;
	private Activity activity;
	private AsyncHttpRequest request;
	private static final String TAG = "MultiPartRequester";

	public MultiPartRequester(Activity activity, Map<String, String> map,
		int serviceCode, AsyncTaskCompleteListener asyncTaskCompleteListener) {
		this.map = map;
		this.serviceCode = serviceCode;
		this.activity = activity;
	}

	class AsyncHttpRequest extends AsyncTask<String, Void, String> {
		@Override
		protected String doInBackground(String... urls) {
			map.remove("url");
			try {
				HttpPost httppost = new HttpPost(urls[0]);
				httpclient = new DefaultHttpClient();
				HttpConnectionParams.setConnectionTimeout(
						httpclient.getParams(), 600000);
				MultipartEntityBuilder builder = MultipartEntityBuilder
						.create();
				for (String key : map.keySet()) {
					if (key.equalsIgnoreCase(AndyConstants.Params.PICTURE)) {
						File f = new File(map.get(key));
						builder.addBinaryBody(key, f,
								ContentType.MULTIPART_FORM_DATA, f.getName());
					} else {
						builder.addTextBody(key, map.get(key), ContentType
								.create("text/plain", MIME.UTF8_CHARSET));
					}
					AppLog.Log(TAG, key + "---->" + map.get(key));
				}
				httppost.setEntity(builder.build());
				ActivityManager manager = (ActivityManager) activity
						.getSystemService(Context.ACTIVITY_SERVICE);
				if (manager.getMemoryClass() < 25) {
					System.gc();
				}
				HttpResponse response = httpclient.execute(httppost);
				String responseBody = EntityUtils.toString(
						response.getEntity(), "UTF-8");
				reurn responseBody;
			} catch (Exception e) {
				e.printStackTrace();
			} catch (OutOfMemoryError oume) {
				System.gc();	
			} finally {
				if (httpclient != null)
					httpclient.getConnectionManager().shutdown();
			}
			return null;
		}

		@Override
		protected void onPostExecute(String response) {
			if (mAsynclistener != null) {
				mAsynclistener.onTaskCompleted(response, serviceCode);
			}
		}
	}
	public void cancelTask() {
		request.cancel(true);
		AppLog.Log(TAG, "task is canelled");
	}
}


Solution

  • this is FileUploadMultipartRequest class:

    /**
     * Multipart request for sending files over http
     * also can return generic type of response data
     * @param <T> the type of data for http responses
     */
    public class FileUploadMultipartRequest<T> extends BaseRequest<T>
    {
        private static final MediaType JSON = MediaType.parse("application/json");
        private File[] files;
        private String jsonString;
        private RequestBody requestBody;
    
        public FileUploadMultipartRequest(String url, Map<String, String> headers, String jsonString, OnEmptyResponseListener listener, ErrorTypeListener errorListener, File... files)
        {
            super(Method.POST, url, headers, listener, new ErrorListenerImpl(errorListener));
            this.jsonString = jsonString;
            this.files = files;
        }
    
        public FileUploadMultipartRequest(String url, Map<String, String> headers, String jsonString, Type responseType, Response.Listener listener, ErrorTypeListener errorListener, File... files)
        {
            super(Method.POST, url, headers, responseType, listener, new ErrorListenerImpl(errorListener));
            this.jsonString = jsonString;
            this.files = files;
        }
    
        @Override
        public String getBodyContentType()
        {
            return buildMultipartEntity().contentType().toString();
        }
    
        @Override
        public byte[] getBody() throws AuthFailureError
        {
            Buffer buffer = new Buffer();
            try
            {
                buildMultipartEntity().writeTo(buffer);
            } catch (IOException e)
            {
                VolleyLog.e("IOException writing to ByteArrayOutputStream");
            }
            return buffer.readByteArray();
        }
    
        private RequestBody buildMultipartEntity()
        {
    
            if (requestBody == null)
            {
                MultipartBuilder multipartBuilder = new MultipartBuilder().type(MultipartBuilder.FORM);
    
                multipartBuilder.addPart(
                        Headers.of("Content-Disposition", "form-data; name=json-part"),
                        RequestBody.create(JSON, jsonString));
    
                for (File file : files)
                {
                    String contentType = URLConnection.guessContentTypeFromName(file.getName());
                    multipartBuilder.addFormDataPart("files-part", file.getName(),
                            RequestBody.create(MediaType.parse(contentType), file));
                }
    
                requestBody = multipartBuilder.build();
            }
            return requestBody;
        }
    
    }
    

    this is BaseRequest class:

    /**
     * this a abstract request class for handling http http responses
     * note : all volley request should extend this class for http request
     *
     * @param <T> the type of data for http responses
     */
    public abstract class BaseRequest<T> extends Request<T>
    {
        private final Map<String, String> headers;
    
        /**
         * the type response that {@link com.android.volley.Response.Listener} should return
         */
        private Type responseType;
    
        /**
         * generic listener for successful http request
         */
        private Response.Listener<T> listener;
    
        /**
         * constructor for request that returns data type {@link T}
         *
         * @param method        http verb e.g. POST, GET & etc
         * @param url           request URL
         * @param headers       http headers
         * @param responseType  type of data that response should return
         * @param listener      event for successful request
         * @param errorListener event for failed request
         */
        public BaseRequest(int method, String url, Map<String, String> headers, Type responseType, Response.Listener listener, ErrorListenerImpl errorListener)
        {
            super(method, url, errorListener);
            this.headers = headers;
            this.responseType = responseType;
            //noinspection unchecked
            this.listener = listener;
        }
    
        /**
         * constructor for requests with no returning data
         *  @param method                  http verb e.g. POST, GET & etc
         * @param url                     request URL
         * @param headers                 http headers
         * @param onEmptyResponseListener event for successful request (but no data return)
         * @param errorListener           event for failed request
         */
        public BaseRequest(int method, String url, Map<String, String> headers, OnEmptyResponseListener onEmptyResponseListener, ErrorListenerImpl errorListener)
        {
            super(method, url, errorListener);
            this.headers = headers;
            //noinspection unchecked
            listener = new OnEmptyResponseImpl(onEmptyResponseListener);
        }
    
        protected Response<T> parseNetworkResponse(NetworkResponse response)
        {
    
            // if response type is null so just pass null to success event
            if (this.responseType == null && new String(response.data).isEmpty())
            {
                return Response.success(null, HttpHeaderParser.parseCacheHeaders(response));
            }
    
            // if response type is specified
            try
            {
                Gson gson = new Gson();
                String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
                // we use GSON to reflect response data to the generic type and pass to success event
                T parseObject = gson.fromJson(json, responseType);
                return Response.success(parseObject, HttpHeaderParser.parseCacheHeaders(response));
            } catch (UnsupportedEncodingException e)
            {
                return Response.error(new ParseError(e));
            } catch (JsonSyntaxException e)
            {
                return Response.error(new ParseError(e));
            }
        }
    
    
        @Override
        protected void deliverResponse(T response)
        {
            if (listener != null)
            {
                // call successful response event when listener not empty
                listener.onResponse(response);
            }
        }
    
        @Override
        protected void onFinish()
        {
            super.onFinish();
            listener = null;
        }
    
        /**
         * this class forward response event to {@link com.khosravi.mehrdadz.garagesale.Network.RequestType.BaseRequest.OnEmptyResponseListener}
         * when volley {@link com.android.volley.Response.Listener} is called
         */
        private static class OnEmptyResponseImpl implements Response.Listener
        {
            OnEmptyResponseListener onEmptyResponseListener;
    
            /**
             * @param onEmptyResponseListener interface for response with not data return
             */
            public OnEmptyResponseImpl(OnEmptyResponseListener onEmptyResponseListener)
            {
                this.onEmptyResponseListener = onEmptyResponseListener;
            }
    
            /**
             * we call {@link com.khosravi.mehrdadz.garagesale.Network.RequestType.BaseRequest.OnEmptyResponseImpl#onEmptyResponseListener}
             * when volley listener is class so no null object passed to the event
             *
             * @param response
             */
            @Override
            public void onResponse(Object response)
            {
                onEmptyResponseListener.OnEmptyResponse();
            }
        }
    
        /**
         * interface for http response with no returning data
         */
        public interface OnEmptyResponseListener
        {
            void OnEmptyResponse();
        }
    
        public Map<String, String> getHeaders() throws AuthFailureError
        {
            return this.headers != null ? this.headers : super.getHeaders();
        }
    }
    

    and this is GsonRequest class:

    @SuppressWarnings("JavaDoc")
    /**
     * Gson request that return generic type of response data
     * @param <T> the type of data for http responses
     */
    public class GsonRequest<T> extends BaseRequest<T>
    {
        protected static final String PROTOCOL_CHARSET = "utf-8";
        /**
         * Content type for request.
         */
        private static final String PROTOCOL_CONTENT_TYPE =
                String.format("application/json; charset=%s", PROTOCOL_CHARSET);
    
        /**
         * message body of http request
         */
        private final String requestBody;
    
        /**
         * Request return response object of Type {@link T}
         * @param url
         * @param headers
         * @param type
         * @param listener
         * @param errorListener
         */
        public GsonRequest(String url, Map<String, String> headers, Type type, Listener<T> listener, ErrorTypeListener errorListener)
        {
            super(Method.GET, url, headers, type, listener, new ErrorListenerImpl(errorListener));
            requestBody = null;
        }
    
        /**
         * Request return response object of Type {@link T}
         * @param url
         * @param headers
         * @param jsonObject json object to send with request
         * @param type
         * @param listener
         * @param errorListener
         */
        public GsonRequest(String url, Map<String, String> headers, JSONObject jsonObject, Type type, Listener<T> listener, ErrorTypeListener errorListener)
        {
            super(Method.POST, url, headers, type, listener, new ErrorListenerImpl(errorListener));
            this.requestBody = jsonObject == null ? null : jsonObject.toString();
        }
    
        /**
         * Request return empty response
         * @param url
         * @param headers
         * @param jsonObject json object to send with request
         * @param listener
         * @param errorListener
         */
        public GsonRequest(String url, Map<String, String> headers, JSONObject jsonObject, OnEmptyResponseListener listener, ErrorTypeListener errorListener)
        {
            super(Method.POST, url, headers, listener, new ErrorListenerImpl(errorListener));
            this.requestBody = jsonObject == null ? null : jsonObject.toString();
        }
    
        /**
         * Request return empty response
         * @param url
         * @param headers
         * @param listener
         * @param errorListener
         */
        public GsonRequest(String url, Map<String, String> headers, BaseRequest.OnEmptyResponseListener listener, ErrorTypeListener errorListener)
        {
            super(Method.GET, url, headers, listener, new ErrorListenerImpl(errorListener));
            requestBody = null;
        }
    
    
        @Override
        public String getBodyContentType()
        {
            return PROTOCOL_CONTENT_TYPE;
        }
    
        @Override
        public byte[] getBody()
        {
            try
            {
                return requestBody == null ? null : requestBody.getBytes(PROTOCOL_CHARSET);
            } catch (UnsupportedEncodingException uee)
            {
                VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
                        requestBody, PROTOCOL_CHARSET);
                return null;
            }
        }
    }
    

    sample cod:

    public class MyRequest
    {
    public MyRequest(Context context)
        {
            volleySingleton = VolleySingleton.getInstance(context);
        }
    private static final String INSERT_NEW_PIC = "INSERT_NEW_PIC";
    public void UploadNewPic(File[] Images, BaseRequest.OnEmptyResponseListener listener, ErrorTypeListener errorListener)
        {
    
            FileUploadMultipartRequest fileUploadMultipartRequest = new FileUploadMultipartRequest("url", null, null, listener, errorListener,Images);
            volleySingleton.addToRequestQueue(fileUploadMultipartRequest, INSERT_NEW_PIC);
        }
    }
    

    you can add more request to my request and call that anywhere. like this:

    MyRequest myRequest;
    private HashMap<FrameLayout,File> Images;
    
    myRequest = new MyRequest(context);
    Images = new HashMap<>();
    
    myRequest.UploadNewPic(Images.values().toArray(new File[Images.values().size()]),  new BaseRequest.OnEmptyResponseListener()
                        {
                            @Override
                            public void OnEmptyResponse()
                            {
                                Toast.makeText(getApplicationContext(), "added pics successfully", Toast.LENGTH_LONG).show();
                                finish();
                            }
                        }, new ErrorTypeListener()
                        {
                            @Override
                            public void onError(ErrorType errorType)
                            {
    
                            }
                        });
    

    server side cod (.net):

    public class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
        {
            public CustomMultipartFormDataStreamProvider(string path) : base(path) { }
    
    
            //below only allows images and pdf files to be uploaded.
            public override Stream GetStream(HttpContent parent, System.Net.Http.Headers.HttpContentHeaders headers)
            {
    
                // following line handles other form fields other than files.
                if (String.IsNullOrEmpty(headers.ContentDisposition.FileName)) return base.GetStream(parent, headers);
    
                // restrict what filetypes can be uploaded
                List<string> extensions = new List<string> { "png", "gif",
                    "jpg", "jpeg", "tiff", "pdf", "tif", "bmp","doc","docx","ods","xls","odt","csv","txt","rtf" };
                var filename = headers.ContentDisposition.FileName.Replace("\"", string.Empty); // correct for chrome.
    
                //make sure it has an extension
                if (filename.IndexOf('.') < 0)
                {
                    return Stream.Null;
                }
    
                //get the extension
                var extension = filename.Split('.').Last();
    
                //Return stream if match otherwise return null stream.
                return extensions.Contains(extension) ? base.GetStream(parent, headers) : Stream.Null;
    
            }
    
            public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
            {
                var name = !string.IsNullOrWhiteSpace(headers.ContentDisposition.FileName) ? headers.ContentDisposition.FileName : "NoName";
                name = name.Replace("\"", string.Empty);
                //name = (Guid.NewGuid()).ToString() +System.IO.Path.GetExtension(name); //this is here because Chrome submits files in quotation marks which get treated as part of the filename and get escaped
    
                name = Path.GetRandomFileName().Replace(".", string.Empty) + Path.GetExtension(name); //this is here because Chrome submits files in quotation marks which get treated as part of the filename and get escaped
    
                return name;
            }
        }
    

    public class ImageRouteHandler :

    IRouteHandler
    {
    
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            var filename = requestContext.RouteData.Values["filename"] as string;
            var section = requestContext.RouteData.Values["section"] as string;
    
            if (string.IsNullOrEmpty(filename) && string.IsNullOrEmpty(section))
            {
                // return a 404 HttpHandler here
                requestContext.HttpContext.Response.StatusCode = 404;
                requestContext.HttpContext.Response.End();
                return null;
            }
    
            requestContext.HttpContext.Response.Clear();
            requestContext.HttpContext.Response.Cache.SetMaxAge(TimeSpan.FromSeconds(500000));
            requestContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.Public);
    
            requestContext.HttpContext.Response.ContentType = GetContentType(filename);
    
            // find physical path to image here. 
            var path = GetPath(section);
    
            if (string.IsNullOrEmpty(path))
            {
                // return a 404 HttpHandler here
                requestContext.HttpContext.Response.StatusCode = 404;
                requestContext.HttpContext.Response.End();
                return null;
            }
    
            var filepath = requestContext.HttpContext.Server.MapPath(path + filename);
            requestContext.HttpContext.Response.WriteFile(filepath);
            requestContext.HttpContext.Response.End();
            return null;
        }
    
        private static string GetPath(string section)
        {
            switch (section)
            {
                case "user":
                    return "~/Resources/Users/";//where you want save pics in project
            }
            return "";
        }
    
    
        private static string GetContentType(string path)
        {
            switch (Path.GetExtension(path))
            {
                case ".bmp":
                    return "Image/bmp";
                case ".gif":
                    return "Image/gif";
                case ".jpg":
                    return "Image/jpeg";
                case ".png":
                    return "Image/png";
            }
            return "";
        }
    }
    

    and change your RouteConfige like this:

    public class RouteConfig
        {
            public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
                // proccess all for protected resources with ImageRouteHandler
                routes.Add("ImagesRoute", new Route("res/{section}/{filename}", new ImageRouteHandler()));
    
                routes.MapRoute(
                    name: "Default",
                    url: "{controller}/{action}/{id}",
                    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                );
            }
        }
    

    sample code for server side:

    public interface IUserManagement
        {
            void UploadNewPics( IEnumerable<string> imageUrls);
        }
    
    
    
        public class UserManagement : IUserManagement
        {
            public void UploadNewPics(IEnumerable<string> imageUrls)
            {
    
                using (var ctx = new Context())
                {
    
                    foreach (var imageUrl in imageUrls)
                    {
                        //save to data base
                        .
                        .
                        .
                    }
    
                    try
                    {
                        ctx.SaveChanges();
                    }
                    catch (Exception e)
                    {
                        throw;
                    }
                }
            }
        }
    
    
    
            public class UserApiController : ApiController
    {
        [HttpPost]
        public async Task<IHttpActionResult> UploadNewPics()
        {
            if (!Request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
            }
    
            var root = HttpContext.Current.Server.MapPath("~/Resources/Users/");
            var provider = new CustomMultipartFormDataStreamProvider(root);
            try
            {
                // Read the form data.
                await Request.Content.ReadAsMultipartAsync(provider);
    
                IUserManagement userManagement = new UserManagement();
    
                var imageUrls = provider.FileData.Select(x=> Path.GetFileName(x.LocalFileName));
                //userManagement.UploadUserImage(uploadImageJson, Path.GetFileName(imageFile.LocalFileName), (long)imageFile.Headers.ContentLength);
                userManagement.UploadNewPics(imageUrls);
            }
            catch (Exception e)
            {
                return InternalServerError();
            }
            return Ok();
        }
    }