So senden Sie mit Volley einen POST mit mehreren Teilen / Formulardaten in Android


89

Hat es schon jemand geschafft, einen multipart/form-dataPOST in Android mit Volley zu senden ? Ich hatte keinen Erfolg beim Versuch, eine image/pngmithilfe einer POST-Anfrage auf unseren Server hochzuladen, und bin neugierig, ob dies jemand getan hat.

Ich glaube, der Standardweg, dies zu tun, wäre, public byte[] getPostBody()die Request.javaKlasse zu überschreiben und sie Filedort mit einem leeren Header-Schlüssel für die Grenze anzuhängen . Das Konvertieren meiner Datei in eine Stringfür die Map<String, String> postParamsund das anschließende erneute Codieren erscheint jedoch stumpf und nicht wirklich elegant. Auch meine Versuche waren erfolglos. Dies ist wirklich das einzige, was uns davon abhält, zu dieser Bibliothek zu wechseln.

Wie auch immer, alle Gedanken und Antworten werden sehr geschätzt. Danke für Ihre Hilfe.

Antworten:


75

Ich könnte mich in dieser Sache irren, aber ich denke, Sie müssen Ihre eigene implementieren, com.android.volley.toolbox.HttpStackda die Standard- HurlStackVersionen ( wenn Version> Lebkuchen oder HttpClientStack) nicht behandelt werden multipart/form-data.

Bearbeiten:

Und tatsächlich habe ich mich geirrt. Ich konnte es mit MultipartEntityRequest wie folgt machen:

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 ist ziemlich roh, aber ich habe es mit einem Bild und einer einfachen Zeichenfolge versucht und es funktioniert. Die Antwort ist ein Platzhalter. In diesem Fall ist es wenig sinnvoll, eine Antwortzeichenfolge zurückzugeben. Ich hatte Probleme mit Apache httpmime, um MultipartEntity zu verwenden, also habe ich diese https://code.google.com/p/httpclientandroidlib/ verwendet. Ich weiß nicht, ob es einen besseren Weg gibt. Ich hoffe es hilft.

Bearbeiten

Sie können httpmime ohne httpclientandroidlib verwenden. Die einzige Abhängigkeit ist httpcore.


2
@LOG_TAG: Dies unterstützt keine großen Dateien. Es wird auch keine Fortschrittsanzeige unterstützen. Grund ist, dass alle Daten in einem einzigen Byte [] zusammengefasst werden. Für große Dateien möchten Sie einen InputStream verwenden, der mit Volley nicht möglich erscheint. Aber sie (Volley-Entwickler) sagen auch, dass Volley nicht für große Dateien gemacht ist.
Patrick Boos

9
@alex können Sie einen Code für die Verwendung von MultipartRequest eingeben?
Krishna Shrestha

4
Ich erhalte diesen Fehler: java.lang.NoClassDefFoundError: org.apache.http.entity.ContentType
Milad

1
MultipartEntity mit Apache http. Ich möchte es ohne Apache Multipart Library tun. Wie kann ich das machen.
JosephM

1
return entity.getContentType (). getValue (); Fehler
Volodymyr Kulyk

14

Wie in der Präsentation im I / O (ca. 4:05) erwähnt, ist Volley für große Nutzlasten "schrecklich". Nach meinem Verständnis bedeutet dies, Volley nicht zum Empfangen / Senden von (großen) Dateien zu verwenden. Wenn man sich den Code ansieht, scheint er nicht einmal für die Verarbeitung mehrteiliger Formulardaten ausgelegt zu sein (z. B. hat Request.java getBodyContentType () mit fest codiertem "application / x-www-form-urlencoded"; HttpClientStack :: createHttpRequest () kann nur Byte verarbeiten [], etc...). Wahrscheinlich können Sie eine Implementierung erstellen, die mehrteilig ist, aber wenn ich Sie wäre, würde ich HttpClient direkt mit MultipartEntity verwenden, wie:

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

Möglicherweise benötigen Sie einen neueren HttpClient (dh nicht den integrierten) oder verwenden Volley mit einem neueren HttpClient


1
Vielen Dank für die Hilfe. Ihr Blog zeigt mir etwas, an das ich nicht gedacht hätte. Ich werde Ihren Vorschlag über den obigen Link versuchen, wenn ich Alex 'Lösung nicht zum Laufen bringen kann. Prost!
AllDayAmazing

Hallo @Ogre_BGR, könnten Sie bitte ein vollständiges Beispiel zum Hochladen eines Bildes mit Ihrem Snap-Code bereitstellen? Ich habe nur viele Proben ausprobiert, aber sie sind sehr alt und ich kann sie nicht zum Laufen bringen. Vielen Dank
Thiago

Multipart wird jetzt unterstützt. Schauen Sie sich die anderen Antworten auf dieser Seite an.
Martin Konecny

1
@ MartinKonecny ​​bitte verknüpfen Sie die richtige Antwort in Ihrem Kommentar, damit andere Benutzer direkt darauf zugreifen können
Ognyan

Wie kann ich in einer einzigen Anfrage weitere Parameter zusammen mit dem mehrteiligen Format hinzufügen?
Casillas

10

UPDATE 26.08.2015:

Wenn Sie kein veraltetes HttpEntity verwenden möchten, finden Sie hier meinen Arbeitsbeispielcode (getestet mit 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);
    }
}

ENDE DES UPDATE

Dies ist mein funktionierender Beispielcode (nur mit kleinen Dateien getestet):

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>

3
Einige Server sind sehr wählerisch. Wenn Sie Probleme haben, fügen Sie ein Leerzeichen zwischen ";" und "Dateiname =" beim Erstellen von Inhaltsdisposition und "mehrteilige / Formulardaten; Grenze =" + Grenze;
Kevin

Danke für deinen Kommentar @Kevin. Eigentlich hatte ich nicht viele Server-Apps, um das zu überprüfen :-)
BNK

@HemsLodha: Bitte werfen Sie einen Blick auf RacZos Antwort auf meine folgende Frage stackoverflow.com/questions/32240177/… um zu sehen, ob es Ihnen helfen kann oder nicht :)
BNK

1
Ich sende eine mehrteilige Post-Anfrage mit Volley und setze den Parameter schließlich auf URL und es funktioniert. Danke für die Hilfe @BNK.
JosephM

@ user3561494 Es wird nicht für große Dateien wie Video empfohlen
BNK

9

Schließen Sie die mehrteilige Anforderung mit dem Upload-Fortschritt ab

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

    }
}

Beispielnutzung

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

    }

Hallo @AZ_ - ich habe Ihren Code ausprobiert: Aber ich erhalte die folgende Fehlermeldung: MultipartRequest.getBody: IOException beim Schreiben in ByteArrayOutputStream. Können Sie bitte helfen
Thiago

Ich habe Ihren Code nicht, ich kann nichts Besseres vorschlagen, um eine andere Frage zu öffnen. Der wahrscheinlichste Grund könnte sein, dass Ihnen der virtuelle Speicher ausgegangen ist.
AZ_

1
Ich habe offen ich neue Frage: stackoverflow.com/questions/31474585/… Können Sie bitte helfen. Vielen Dank
Thiago

Wie kann ich in einer einzigen Anfrage weitere Parameter zusammen mit dem mehrteiligen Format hinzufügen?
Casillas

@casillas fügt dem Header der Anfrage ein Schlüsselwertpaar hinzu.
AZ_

8

Ein sehr einfacher Ansatz für Entwickler, die nur POST-Parameter in mehrteiligen Anforderungen senden möchten.

Nehmen Sie die folgenden Änderungen in der Klasse vor, die Request.java erweitert

Definieren Sie zuerst diese Konstanten:

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;

Fügen Sie eine Hilfsfunktion hinzu, um einen Post-Body für Sie zu erstellen:

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

Überschreiben Sie getBody () und getBodyContentType

public String getBodyContentType() {
    return MULTIPART_FORMDATA;
}

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

1
Sehr einfach und funktioniert perfekt! Ich musste sbPost.append("--" + BOUNDARY + "--");kurz vor meiner Rückkehr, da die von mir verwendete API ein schließendes Tag erfordert
Sirens

1
Vielen Dank :)
Arpit Ratan

Vielen Dank! Dies hat bei mir im Front-End funktioniert, aber das Back-End, speziell nodejs, hat Probleme beim Parsen? Hast du etwas davon erlebt?
Woppi

1
Entschuldigung, ich habe keine Backend-Erfahrung :(
Arpit Ratan

@Arpit danke, ich konnte mein Problem lösen, indem ich diese Hilfsklasse
Woppi verwendete

4

Erste Antwort auf SO.

Ich bin auf dasselbe Problem gestoßen und fand den Code von @alex sehr hilfreich. Ich habe einige einfache Änderungen vorgenommen, um so viele Parameter wie nötig über HashMap zu übergeben, und habe im Grunde genommen parseNetworkResponse()von StringRequest kopiert . Ich habe online gesucht und war so überrascht, dass eine so häufige Aufgabe so selten beantwortet wird. Wie auch immer, ich wünschte, der Code könnte helfen:

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

Und Sie können die Klasse wie folgt verwenden:

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

0

Eine andere Lösung, sehr leicht mit hoher Leistung bei großer Nutzlast:

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

            };

        }

    );

}

2
Große Dateien geben nicht genügend Speicher aus
Ravi

1
@ Ravi +1 diese Bibliothek ist zum Hochladen großer Dateien zum Kotzen
Anton

Wenn Sie große Dateien senden möchten, verwenden Sie Apache-MIME-Bibliotheken für mehrere Teile und veröffentlichen Sie sie dann mit dieser Bibliothek.
Ravi

Sie müssen die Größe des Bildes komprimieren und dann diese Bibliothek verwenden, um das Bild hochzuladen. Ich habe die Lösung unter der folgenden URL gefunden: programmerguru.com/android-tutorial/… Ich hoffe, dies kann jemandem Zeit sparen.
Milan Sheth

@ Ravi Apache Mime gibt Ihnen Konflikte und doppelte Klassenfehler.
Muhammad Saqib

0

Hier ist eine einfache Lösung und ein vollständiges Beispiel für das Hochladen von Dateien mit Volley Android

1) Gradle-Import

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

2) Erstellen Sie nun einen 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) Erstellen Sie jetzt eine Klasse für die Anforderung zum Hochladen von 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) Rufen Sie jetzt die folgende Methode auf, um den Dienst zu aktivieren

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

Ich erhalte diesen Fehler mit Ihrer Lösung: java.lang.NoSuchMethodError: Keine direkte Methode <init> (ILjava / lang / String; Lcom / android / volley / Request $ Priority; Lcom / android / volley / Response $ ErrorListener; Lcom / android / volley / RetryPolicy;) V in der Klasse Lcom / android / volley / Request; oder seine Superklassen (Deklaration von 'com.android.volley.Request' erscheint in /data/data/com.footballscout.app/files/instant-run/dex/slice-slice_4-classes.dex)
SpyZip

Haben Sie in den Klassen nach dem richtigen Gradle und den richtigen Importen gesucht?
Schneller Lerner

Ich habe die hier einmal beschriebene und Ihre Antwort github.com/DWorkS/VolleyPlus
SpyZip

1
und überprüfen Sie Ihre Importe in der Klasse, in der Sie sie verwenden, da Sie möglicherweise nicht die richtigen Importe für Volley verwenden, da der Fehler auf den Fehler-Listener zeigt. Überprüfen Sie die Importe erneut und schließen Sie das Projekt ab, danke, falls Sie noch welche finden Problem lassen Sie es mich wissen
Schneller Lerner

-1

Das ist meine Art, es zu tun. Es kann für andere nützlich sein:

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

 }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.