volley studio peticiones example android http android-volley

studio - volley android example



Cómo enviar un POST "multipart/form-data" en Android con Volley (9)

Solicitud de múltiples partes completa con progreso de carga

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

Uso de la muestra

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

¿Alguien ha podido enviar un POST multipart/form-data en Android con Volley todavía? No he tenido éxito al intentar cargar una image/png con una solicitud POST a nuestro servidor y tengo curiosidad por saber si alguien lo hizo.

Creo que la forma predeterminada de hacer esto sería anular public byte[] getPostBody() en la clase Request.java y adjuntar el File allí con una clave de encabezado en blanco para el límite. Sin embargo, la conversión de mi archivo a una String para el Map<String, String> postParams y luego tener codificado de nuevo parece obtuso y no es realmente elegante. También he fracasado en mis intentos. Esto es realmente lo único que nos impide cambiar a esta biblioteca.

De todos modos, todos los pensamientos y respuestas son muy apreciados. Gracias por tu ayuda.


Aquí hay una solución simple y un ejemplo completo para cargar archivos usando Volley Android

1) Gradle Import

compile ''dev.dworks.libs:volleyplus:+''

2) Ahora crea un 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) Ahora crea una clase para gestionar la solicitud de carga de 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) Y ahora llame al método Like This para acceder al servicio

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


Como se mencionó en la presentación en la E / S (alrededor de las 4:05), Volley "es terrible" para grandes cargas útiles. Según tengo entendido, eso significa no usar Volley para recibir / enviar (grandes) archivos. Al mirar el código, parece que ni siquiera está diseñado para manejar datos de formularios de varias partes (por ejemplo, Request.java tiene getBodyContentType () con el código fijo "application / x-www-form-urlencoded"; HttpClientStack :: createHttpRequest () solo puede manejar byte [], etc ...). Probablemente podrá crear una implementación que pueda manejar varias partes, pero si yo fuera usted, simplemente usaré HttpClient directamente con MultipartEntity como:

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

Es posible que necesite HttpClient más nuevo (es decir, no incorporado) o incluso mejor, use Volley con HttpClient más nuevo.


Esta es mi forma de hacerlo. Puede ser útil para otros:

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


Otra solución, muy ligera con alto rendimiento con carga útil grande:

Biblioteca de Android Asynchronous Http Client: 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!"); }; } ); }


Primera respuesta en SO.

He encontrado el mismo problema y encontré que el código de @alex es muy útil. He hecho algunas modificaciones simples para pasar tantos parámetros como sea necesario a través de HashMap, y básicamente he copiado parseNetworkResponse() de StringRequest. He buscado en línea y estoy tan sorprendido de descubrir que una tarea tan común rara vez se responde. De todos modos, me gustaría que el código pudiera ayudar:

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

Y puedes usar la clase de la siguiente manera:

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


Puede que me equivoque al respecto, pero creo que debes implementar tu propio com.android.volley.toolbox.HttpStack porque los predeterminados ( HurlStack si versión> Gingerbread o HttpClientStack ) no tratan con multipart/form-data .

Editar:

Y de hecho estaba equivocado. Pude hacerlo usando MultipartEntity en Request como este:

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

Es bastante crudo, pero lo probé con una imagen y una cadena simple y funciona. La respuesta es un marcador de posición, no tiene mucho sentido devolver una cadena de respuesta en este caso. Tuve problemas al utilizar apache httpmime para usar MultipartEntity, así que utilicé esta https://code.google.com/p/httpclientandroidlib/ no sé si hay una forma mejor. Espero eso ayude.

Editar

Puede usar httpmime sin usar httpclientandroidlib, la única dependencia es httpcore.


Un enfoque muy simple para el desarrollador que solo desea enviar parámetros POST en solicitudes de varias partes.

Realice los siguientes cambios en la clase que extiende Request.java

Primero defina estas constantes:

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;

Agregue una función auxiliar para crear un cuerpo de publicación para usted:

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

Anular getBody () y getBodyContentType

public String getBodyContentType() { return MULTIPART_FORMDATA; } public byte[] getBody() throws AuthFailureError { return createPostBody(getParams()).getBytes(); }


ACTUALIZACIÓN 26/08/2015:

Si no quiere usar HttpEntity en desuso, aquí está mi código de ejemplo de trabajo (probado con 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); } }

FIN DE ACTUALIZACIÓN

Este es mi código de muestra de trabajo (solo probado con archivos de tamaño pequeño):

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>