java - volleymultipartrequest - Solicitud POST de mĂșltiples partes de trabajo con Volley y sin HttpEntity
volley send file to server (5)
Esto no es realmente una pregunta, sin embargo, me gustaría compartir algo de mi código de trabajo aquí para su referencia cuando lo necesite.
Como sabemos,
HttpEntity
está en desuso de API22 y se elimina por completo desde API23.
Por el momento, ya no podemos acceder a
HttpEntity Reference en Android Developer
(404).
Entonces, el siguiente es mi código de muestra de trabajo para
POST Multipart Request con Volley y sin HttpEntity
.
Está funcionando, probado con
Asp.Net Web API
.
Por supuesto, el código tal vez sea solo una muestra básica que publica dos archivos dibujables existentes, tampoco es la mejor solución para todos los casos y no es un buen ajuste.
MultipartActivity.java:
package com.example.multipartvolley;
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 android.widget.Toast;
import com.android.volley.NetworkResponse;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class MultipartActivity extends Activity {
private final Context context = this;
private final String twoHyphens = "--";
private final String lineEnd = "/r/n";
private final String boundary = "apiclient-" + System.currentTimeMillis();
private final String mimeType = "multipart/form-data;boundary=" + boundary;
private byte[] multipartBody;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_multipart);
byte[] fileData1 = getFileDataFromDrawable(context, R.drawable.ic_action_android);
byte[] fileData2 = getFileDataFromDrawable(context, R.drawable.ic_action_book);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
try {
// the first file
buildPart(dos, fileData1, "ic_action_android.png");
// the second file
buildPart(dos, fileData2, "ic_action_book.png");
// send multipart form data necesssary after file data
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
// pass to multipart body
multipartBody = bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
String url = "http://192.168.1.100/api/postfile";
MultipartRequest multipartRequest = new MultipartRequest(url, null, mimeType, multipartBody, new Response.Listener<NetworkResponse>() {
@Override
public void onResponse(NetworkResponse response) {
Toast.makeText(context, "Upload successfully!", Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(context, "Upload failed!/r/n" + error.toString(), Toast.LENGTH_SHORT).show();
}
});
VolleySingleton.getInstance(context).addToRequestQueue(multipartRequest);
}
@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);
}
private void buildPart(DataOutputStream dataOutputStream, byte[] fileData, String fileName) throws IOException {
dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
dataOutputStream.writeBytes("Content-Disposition: form-data; name=/"uploaded_file/"; filename=/""
+ fileName + "/"" + lineEnd);
dataOutputStream.writeBytes(lineEnd);
ByteArrayInputStream fileInputStream = new ByteArrayInputStream(fileData);
int bytesAvailable = fileInputStream.available();
int maxBufferSize = 1024 * 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
byte[] buffer = new byte[bufferSize];
// read file and write it into form...
int bytesRead = fileInputStream.read(buffer, 0, bufferSize);
while (bytesRead > 0) {
dataOutputStream.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
dataOutputStream.writeBytes(lineEnd);
}
private byte[] getFileDataFromDrawable(Context context, int id) {
Drawable drawable = ContextCompat.getDrawable(context, id);
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 0, byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
}
}
MultipartRequest.java:
package com.example.multipartvolley;
import com.android.volley.AuthFailureError;
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 java.util.Map;
class MultipartRequest extends Request<NetworkResponse> {
private final Response.Listener<NetworkResponse> mListener;
private final Response.ErrorListener mErrorListener;
private final Map<String, String> mHeaders;
private final String mMimeType;
private final byte[] mMultipartBody;
public MultipartRequest(String url, Map<String, String> headers, String mimeType, byte[] multipartBody, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) {
super(Method.POST, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
this.mHeaders = headers;
this.mMimeType = mimeType;
this.mMultipartBody = multipartBody;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return (mHeaders != null) ? mHeaders : super.getHeaders();
}
@Override
public String getBodyContentType() {
return mMimeType;
}
@Override
public byte[] getBody() throws AuthFailureError {
return mMultipartBody;
}
@Override
protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) {
try {
return Response.success(
response,
HttpHeaderParser.parseCacheHeaders(response));
} catch (Exception e) {
return Response.error(new ParseError(e));
}
}
@Override
protected void deliverResponse(NetworkResponse response) {
mListener.onResponse(response);
}
@Override
public void deliverError(VolleyError error) {
mErrorListener.onErrorResponse(error);
}
}
ACTUALIZAR:
Para la parte del texto, consulte la respuesta de @ Oscar a continuación.
Aquí hay una versión de Kotlin de una clase que permite la solicitud multiparte con Volley 1.1.1.
Se basa principalmente en la solución de @ BNK, pero está ligeramente simplificada. No noté ningún problema de rendimiento en particular. Subí una foto de 5Mb en unos 3 segundos.
dependencies {
compile ''dev.dworks.libs:volleyplus:+''
}
Encontré un contenedor de la biblioteca de volley original que es más fácil de integrar para solicitudes de varias partes. También admite la carga de datos de varias partes junto con otros parámetros de solicitud. Por lo tanto, estoy compartiendo mi código para los futuros desarrolladores que podrían encontrarse con el problema que estaba teniendo (es decir, cargar datos de varias partes usando volley junto con algunos otros parámetros).
Agregue la siguiente biblioteca en el archivo
build.gradle
.
public class POSTMediasTask {
public void uploadMedia(final Context context, String filePath) {
String url = getUrlForPOSTMedia(); // This is a dummy function which returns the POST url for you
SimpleMultiPartRequest multiPartRequestWithParams = new SimpleMultiPartRequest(Request.Method.POST, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("Response", response);
// TODO: Do something on success
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// TODO: Handle your error here
}
});
// Add the file here
multiPartRequestWithParams.addFile("file", filePath);
// Add the params here
multiPartRequestWithParams.addStringParam("param1", "SomeParamValue1");
multiPartRequestWithParams.addStringParam("param2", "SomeParamValue2");
RequestQueue queue = Volley.newRequestQueue(context);
queue.add(multiPartRequestWithParams);
}
}
Tenga en cuenta que,
build.gradle
biblioteca de volley original de mi
build.gradle
y usé la biblioteca anterior en su lugar, que puede manejar tanto las solicitudes de varias partes como las normales con una técnica de integración similar.
Luego solo tuve que escribir la siguiente clase que maneja la operación de solicitud POST.
new POSTMediasTask().uploadMedia(context, mediaPath);
Ahora ejecute la tarea de la siguiente manera.
new POSTMediasTask().uploadMedia(context, mediaPath);
Puede cargar un archivo a la vez usando esta biblioteca. Sin embargo, pude subir varios archivos simplemente iniciando varias tareas.
¡Espero que ayude!
Para aquellos que tienen dificultades para enviar parámetros utf-8 y todavía no tienen suerte, el problema que tuve fue en dataOutputStream, y cambié el código de @RacZo al siguiente código:
private void buildTextPart(DataOutputStream dataOutputStream, String parameterName, String parameterValue) throws IOException {
dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
dataOutputStream.writeBytes("Content-Disposition: form-data; name=/"");
dataOutputStream.write(parameterName.getBytes("UTF-8"));
dataOutputStream.writeBytes(lineEnd);
dataOutputStream.writeBytes("Content-Type: text/plain; charset=UTF-8" + lineEnd);
dataOutputStream.writeBytes(lineEnd);
dataOutputStream.write(parameterValue.getBytes("UTF-8"));
dataOutputStream.writeBytes(lineEnd);
}
Reescribo su código @RacZo y @BNK más modular y fácil de usar como
VolleyMultipartRequest multipartRequest = new VolleyMultipartRequest(Request.Method.POST, url, new Response.Listener<NetworkResponse>() {
@Override
public void onResponse(NetworkResponse response) {
String resultResponse = new String(response.data);
// parse success output
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("api_token", "gh659gjhvdyudo973823tt9gvjf7i6ric75r76");
params.put("name", "Angga");
params.put("location", "Indonesia");
params.put("about", "UI/UX Designer");
params.put("contact", "[email protected]");
return params;
}
@Override
protected Map<String, DataPart> getByteData() {
Map<String, DataPart> params = new HashMap<>();
// file name could found file base or direct access from real path
// for now just get bitmap data from ImageView
params.put("avatar", new DataPart("file_avatar.jpg", AppHelper.getFileDataFromDrawable(getBaseContext(), mAvatarImage.getDrawable()), "image/jpeg"));
params.put("cover", new DataPart("file_cover.jpg", AppHelper.getFileDataFromDrawable(getBaseContext(), mCoverImage.getDrawable()), "image/jpeg"));
return params;
}
};
VolleySingleton.getInstance(getBaseContext()).addToRequestQueue(multipartRequest);
Verifique el código completo
VolleyMultipartRequest
en mi
gist
.
Solo quiero agregar a la respuesta. Estaba tratando de imaginar cómo agregar campos de texto al cuerpo y creé la siguiente función para hacerlo:
private void buildTextPart(DataOutputStream dataOutputStream, String parameterName, String parameterValue) throws IOException {
dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
dataOutputStream.writeBytes("Content-Disposition: form-data; name=/"" + parameterName + "/"" + lineEnd);
dataOutputStream.writeBytes("Content-Type: text/plain; charset=UTF-8" + lineEnd);
dataOutputStream.writeBytes(lineEnd);
dataOutputStream.writeBytes(parameterValue + lineEnd);
}
Funciona bastante bien.