Harvi Sirja Harvi Sirja - 1 month ago 22
JSON Question

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




Answer

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