Comment envoyer un POST «multipart / form-data» sous Android avec Volley

89

Quelqu'un a-t-il encore réussi à envoyer un multipart/form-dataPOST sous Android avec Volley? Je n'ai pas réussi à télécharger une image/pngrequête POST sur notre serveur et je suis curieux de savoir si quelqu'un l'a fait.

Je pense que la façon par défaut de le faire serait de remplacer public byte[] getPostBody()dans la Request.javaclasse et de l'attacher Fileavec une clé d'en-tête vide pour la limite. Cependant, convertir mon fichier en un Stringpour le Map<String, String> postParamspuis le faire encoder à nouveau semble obtus et pas vraiment élégant. J'ai également échoué dans mes tentatives. C'est vraiment la seule chose qui nous empêche de passer à cette bibliothèque.

Quoi qu'il en soit, toutes les pensées et réponses sont extrêmement appréciées. Merci de votre aide.

AllDayAmazing
la source

Réponses:

75

Je me trompe peut-être, mais je pense que vous devez implémenter les vôtres com.android.volley.toolbox.HttpStackpour cela, car ceux par défaut ( HurlStacksi version> Gingerbread ou HttpClientStack) ne fonctionnent pas multipart/form-data.

Éditer:

Et en effet, j'avais tort. J'ai pu le faire en utilisant MultipartEntitydans Request comme ceci:

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

C'est assez brut mais je l'ai essayé avec une image et une simple chaîne et ça marche. La réponse est un espace réservé, cela n'a pas beaucoup de sens de renvoyer une chaîne de réponse dans ce cas. J'ai eu des problèmes avec apache httpmime pour utiliser MultipartEntity, j'ai donc utilisé ce https://code.google.com/p/httpclientandroidlib/ ne sais pas s'il existe un meilleur moyen. J'espère que ça aide.

Éditer

Vous pouvez utiliser httpmime sans utiliser httpclientandroidlib, la seule dépendance est httpcore.

Alex
la source
2
@LOG_TAG: Cela ne prend pas en charge les gros fichiers. Il ne supportera pas non plus une barre de progression. La raison est qu'il mettra toutes les données dans un seul octet []. Pour les gros fichiers, vous souhaitez utiliser un InputStream, ce qui ne semble pas possible avec volley. Mais ils (les développeurs de volley) disent aussi que la volley n'est pas faite pour les gros fichiers.
Patrick Boos
9
@alex pouvez-vous mettre du code pour utiliser MultipartRequest?
Krishna Shrestha
4
J'obtiens cette erreur: java.lang.NoClassDefFoundError: org.apache.http.entity.ContentType
Milad
1
MultipartEntity utilisant apache http. je veux le faire sans bibliothèque multipart apache. Comment puis-je faire ceci.
JosephM
1
return entity.getContentType (). getValue (); Erreur
Volodymyr Kulyk
14

Comme mentionné dans la présentation aux E / S (environ 4h05), Volley "est terrible" pour les charges utiles importantes. Si je comprends bien, cela signifie ne pas utiliser Volley pour recevoir / envoyer de (gros) fichiers. En regardant le code, il semble qu'il n'est même pas conçu pour gérer les données de formulaire en plusieurs parties (par exemple, Request.java a getBodyContentType () avec "application / x-www-form-urlencoded" codé en dur; HttpClientStack :: createHttpRequest () ne peut gérer que l'octet [], etc...). Vous pourrez probablement créer une implémentation capable de gérer le multipart mais si j'étais vous, je vais simplement utiliser HttpClient directement avec MultipartEntity comme:

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

Vous aurez peut-être besoin d' un HttpClient plus récent (c'est-à-dire pas du module intégré) ou encore mieux, utilisez Volley avec le plus récent HttpClient

Ognyan
la source
1
Merci beaucoup pour l'aide, votre blog me montre quelque chose que je n'aurais pas pensé. J'essaierai votre suggestion via le lien ci-dessus si je ne parviens pas à faire fonctionner la solution d'Alex. À votre santé!
AllDayAmazing
Bonjour @Ogre_BGR, Pourriez-vous s'il vous plaît fournir un exemple complet comment télécharger une image en utilisant votre code de capture. J'ai essayé de nombreux échantillons uniquement, mais ils sont très anciens et je ne peux pas les faire fonctionner. Merci
Thiago
Multipart est désormais pris en charge. Regardez les autres réponses sur cette page.
Martin Konecny
1
@MartinKonecny ​​s'il vous plaît lier la bonne réponse dans votre commentaire afin que les autres utilisateurs puissent y accéder directement
Ognyan
Comment pourrais-je être en mesure d'ajouter d'autres paramètres avec le format multipart en une seule demande?
casillas
10

MISE À JOUR 2015/08/26:

Si vous ne souhaitez pas utiliser HttpEntity obsolète, voici mon exemple de code de travail (testé avec 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 MISE À JOUR

Voici mon exemple de code de travail (testé uniquement avec des fichiers de petite taille):

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>
BNK
la source
3
Certains serveurs sont très difficiles. Si vous rencontrez des problèmes, ajoutez un ESPACE entre ";" et "filename =" lors de la construction de Content-Disposition et "multipart / form-data; boundary =" + boundary;
Kevin le
Merci pour votre commentaire @Kevin. En fait, je n'ai pas eu beaucoup d'applications serveur pour vérifier cela :-)
BNK
@HemsLodha: veuillez jeter un oeil à la réponse de RacZo à ma question suivante stackoverflow.com/questions/32240177/… pour voir si cela peut vous aider ou non :)
BNK
1
J'envoie une demande post-multipart avec volley et définissez enfin le paramètre sur url et cela fonctionne. Merci pour l'aide @BNK.
JosephM
@ user3561494 ce n'est pas recommandé pour les fichiers de grande taille tels que la vidéo
BNK
9

Demande en plusieurs parties avec progression du téléchargement

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

    }
}

Exemple d'utilisation

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

    }
AZ_
la source
Salut @AZ_ - j'ai essayé votre code: mais j'obtiens cette erreur: MultipartRequest.getBody: IOException écrivant à ByteArrayOutputStream. Pouvez-vous aider s'il vous plaît
Thiago
Je n'ai pas votre code, je ne peux rien suggérer de mieux pour ouvrir une autre question, la raison la plus probable pourrait être que vous n'avez plus de mémoire virtuelle.
AZ_
1
J'ai ouvert une nouvelle Question: stackoverflow.com/questions/31474585/... Pouvez-vous aider pls. merci
Thiago
Comment pourrais-je être en mesure d'ajouter d'autres paramètres avec le format multipart en une seule demande?
casillas
@casillas ajoute une paire clé / valeur à l'en-tête de la requête.
AZ_
8

Une approche très simple pour les développeurs qui souhaitent simplement envoyer des paramètres POST en requête multipart.

Apportez les modifications suivantes dans la classe qui étend Request.java

Définissez d'abord ces 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;

Ajoutez une fonction d'assistance pour créer un corps d'article pour vous:

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

Remplacer getBody () et getBodyContentType

public String getBodyContentType() {
    return MULTIPART_FORMDATA;
}

public byte[] getBody() throws AuthFailureError {
        return createPostBody(getParams()).getBytes();
}
Arpit Ratan
la source
1
Très simplement et fonctionne parfaitement! Je devais sbPost.append("--" + BOUNDARY + "--");juste avant de revenir car l'API que j'utilise nécessite une balise de fermeture
Sirens
1
Merci beaucoup :)
Arpit Ratan
Merci! Cela a fonctionné pour moi sur le front-end mais le back-end spécifiquement nodejs a du mal à l'analyser? Avez-vous vécu cela?
Woppi
1
Désolé, je n'ai pas d'expérience backend :(
Arpit Ratan
@Arpit merci, j'ai pu résoudre mon problème en utilisant cette classe d'aide à la place gist.github.com/anggadarkprince
...
4

Première réponse sur SO.

J'ai rencontré le même problème et j'ai trouvé le code de @alex très utile. J'ai fait quelques modifications simples afin de transmettre autant de paramètres que nécessaire via HashMap, et j'ai essentiellement copié parseNetworkResponse()depuis StringRequest. J'ai cherché en ligne et j'ai été tellement surpris de découvrir qu'une tâche aussi courante est si rarement traitée. Quoi qu'il en soit, je souhaite que le code puisse aider:

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

Et vous pouvez utiliser la classe comme suit:

    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);
Michael Shang
la source
0

Autre solution, très légère avec des performances élevées avec une charge utile importante:

Bibliothèque client Http asynchrone Android: 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!");

            };

        }

    );

}
Hpsaturn
la source
2
les grandes tailles de fichiers donnent une exception de mémoire
Ravi
1
@Ravi +1 cette bibliothèque craint pour le téléchargement de gros fichiers
Anton
si vous voulez envoyer des fichiers volumineux, utilisez les bibliothèques mime apache pour les parties multiples, puis publiez-les en utilisant cette bibliothèque.
Ravi
Vous devez compresser la taille de l'image, puis utiliser cette bibliothèque afin de télécharger l'image que j'ai trouvé une solution à partir de l'URL ci-dessous: programmerguru.com/android-tutorial/ ... J'espère que cela peut faire gagner du temps à quelqu'un.
Milan Sheth
@Ravi apache mime vous donnera une erreur de conflit et de classe en double.
Muhammad Saqib le
0

Voici une solution simple et un exemple complet pour télécharger un fichier à l'aide de Volley Android

1) Importation Gradle

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

2) Créez maintenant un RequestManager de classe

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) Créez maintenant une classe pour gérer la demande de téléchargement de fichier 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) Et maintenant, appelez la méthode comme celle-ci pour frapper le service

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 
    }
}
Apprenant rapide
la source
J'obtiens cette erreur avec votre solution: java.lang.NoSuchMethodError: Pas de méthode directe <init> (ILjava / lang / String; Lcom / android / volley / Request $ Priority; Lcom / android / volley / Response $ ErrorListener; Lcom / android / volley / RetryPolicy;) V dans la classe Lcom / android / volley / Request; ou ses super classes (la déclaration de 'com.android.volley.Request' apparaît dans /data/data/com.footballscout.app/files/instant-run/dex/slice-slice_4-classes.dex)
SpyZip
avez-vous vérifié le grade correct et les importations dans les classes?
Apprentissage rapide
J'ai utilisé la fois décrite ici et votre réponse github.com/DWorkS/VolleyPlus
SpyZip
1
et vérifiez vos importations dans la classe où que vous soyez, car vous ne devez pas utiliser les importations correctes pour volley, comme l'erreur pointe sur l'écouteur d'erreur, vérifiez à nouveau les importations et notez puis nettoyez le projet, merci, si vous en trouvez toujours problème, faites-le moi savoir
Apprentissage rapide
-1

C'est ma façon de faire. Cela peut être utile à d'autres:

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

 }
koteswara DK
la source