วิธีส่งโพสต์แบบ“ หลายส่วน / แบบฟอร์มข้อมูล” ใน Android ด้วย Volley


89

มีใครสามารถส่งmultipart/form-dataPOST ใน Android ด้วย Volley ได้สำเร็จหรือยัง? ฉันไม่ประสบความสำเร็จในการอัปโหลดimage/pngโดยใช้คำขอ POST ไปยังเซิร์ฟเวอร์ของเราและฉันอยากรู้ว่ามีใครบ้าง

ฉันเชื่อว่าวิธีเริ่มต้นในการดำเนินการนี้คือการลบล้างpublic byte[] getPostBody()ในRequest.javaคลาสและแนบFileคีย์ส่วนหัวว่างสำหรับขอบเขต อย่างไรก็ตามการแปลงไฟล์ของฉันเป็นไฟล์StringสำหรับMap<String, String> postParamsแล้วเข้ารหัสอีกครั้งดูเหมือนจะป้านและไม่สวยงามจริงๆ นอกจากนี้ฉันยังไม่ประสบความสำเร็จในความพยายามของฉัน นี่เป็นสิ่งเดียวที่ทำให้เราไม่เปลี่ยนมาใช้ห้องสมุดนี้

อย่างไรก็ตามความคิดและคำตอบทั้งหมดเป็นที่ชื่นชมอย่างยิ่ง ขอขอบคุณสำหรับความช่วยเหลือของคุณ.

คำตอบ:


75

ฉันอาจจะไม่ถูกต้องเกี่ยวกับเรื่องนี้ แต่ฉันคิดว่าคุณจำเป็นต้องใช้ของคุณเองcom.android.volley.toolbox.HttpStackสำหรับเรื่องนี้เพราะคนที่เริ่มต้น ( HurlStackหากรุ่น> Gingerbread หรือHttpClientStack) multipart/form-dataไม่ได้จัดการกับ

แก้ไข:

และแน่นอนฉันคิดผิด ฉันสามารถทำได้โดยใช้MultipartEntityในคำขอเช่นนี้:

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

มันค่อนข้างดิบ แต่ฉันลองใช้รูปภาพและสตริงง่ายๆแล้วมันก็ใช้ได้ การตอบกลับเป็นตัวยึดตำแหน่งไม่สมเหตุสมผลที่จะส่งคืนสตริงการตอบกลับในกรณีนี้ ฉันมีปัญหาในการใช้ apache httpmime เพื่อใช้ MultipartEntity ดังนั้นฉันจึงใช้https://code.google.com/p/httpclientandroidlib/นี้ไม่รู้ว่ามีวิธีที่ดีกว่านี้หรือไม่ หวังว่าจะช่วยได้

แก้ไข

คุณสามารถใช้ httpmime ได้โดยไม่ต้องใช้ httpclientandroidlib การพึ่งพาเพียงอย่างเดียวคือ httpcore


2
@LOG_TAG: สิ่งนี้ไม่รองรับไฟล์ขนาดใหญ่ และจะไม่รองรับแถบความคืบหน้า เหตุผลคือจะทำให้ข้อมูลทั้งหมดอยู่ในไบต์เดียว [] สำหรับไฟล์ขนาดใหญ่คุณต้องการใช้ InputStream ซึ่งดูเหมือนจะเป็นไปไม่ได้กับวอลเลย์ แต่พวกเขา (นักวอลเลย์) ก็บอกเช่นกันว่าวอลเลย์ไม่ได้สร้างมาเพื่อไฟล์ขนาดใหญ่
Patrick Boos

9
@alex คุณสามารถใส่รหัสวิธีใช้ MultipartRequest ได้หรือไม่?
Krishna Shrestha

5
ฉันได้รับข้อผิดพลาดนี้: java.lang.NoClassDefFoundError: org.apache.http.entity.ContentType
Milad

1
MultipartEntity โดยใช้ apache http ฉันต้องการทำโดยไม่มีไลบรารีหลายส่วนของ apache ฉันจะทำอย่างไร
JosephM

1
ส่งคืน entity.getContentType (). getValue (); ข้อผิดพลาด
Volodymyr Kulyk

14

ดังที่ได้กล่าวไว้ในการนำเสนอที่ I / O (ประมาณ 04:05) วอลเลย์ "แย่มาก" สำหรับน้ำหนักบรรทุกขนาดใหญ่ ตามที่ฉันเข้าใจนั่นหมายถึงอย่าใช้ Volley ในการรับ / ส่งไฟล์ (ใหญ่) เมื่อมองไปที่โค้ดดูเหมือนว่ามันไม่ได้ออกแบบมาเพื่อจัดการกับข้อมูลในรูปแบบหลายส่วน (เช่น Request.java มี getBodyContentType () พร้อมฮาร์ดโค้ด "application / x-www-form-urlencoded"; HttpClientStack :: createHttpRequest () สามารถจัดการได้เฉพาะไบต์เท่านั้น [] ฯลฯ ... ) คุณอาจจะสามารถสร้างการใช้งานที่สามารถจัดการหลายส่วนได้ แต่ถ้าฉันเป็นคุณฉันจะใช้ HttpClient โดยตรงกับ MultipartEntity เช่น:

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

คุณอาจต้องการHttpClient ที่ใหม่กว่า (เช่นไม่ใช่ในตัว) หรือดีกว่านั้นให้ใช้ Volley กับ HttpClient ที่ใหม่กว่า


1
ขอบคุณมากสำหรับความช่วยเหลือบล็อกของคุณแสดงสิ่งที่ฉันไม่มี ฉันจะลองทำตามคำแนะนำของคุณผ่านลิงค์ด้านบนหากไม่สามารถใช้วิธีแก้ปัญหาของ Alex ได้ ไชโย!
AllDayAmazing

สวัสดี @Ogre_BGR คุณช่วยกรุณาให้ตัวอย่างวิธีการอัปโหลดภาพโดยใช้รหัสสแน็ปอิน ฉันลองใช้หลายตัวอย่างเท่านั้น แต่มันเก่ามากและฉันไม่สามารถใช้งานได้ ขอบคุณ
Thiago

รองรับ Multipart แล้ว ดูคำตอบอื่น ๆ ในหน้านี้
Martin Konecny

1
@MartinKonecny ​​โปรดเชื่อมโยงคำตอบที่ถูกต้องในความคิดเห็นของคุณเพื่อให้ผู้ใช้รายอื่นสามารถเข้าถึงได้โดยตรง
Ognyan

ฉันจะเพิ่มพารามิเตอร์อื่น ๆ พร้อมกับรูปแบบหลายส่วนในคำขอเดียวได้อย่างไร
casillas

10

อัพเดท 2015/08/26:

ถ้าคุณไม่ต้องการใช้ HttpEntity ที่เลิกใช้แล้วนี่คือโค้ดตัวอย่างการทำงานของฉัน (ทดสอบด้วย 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);
    }
}

สิ้นสุดการอัปเดต

นี่คือโค้ดตัวอย่างที่ใช้งานได้ของฉัน (ทดสอบกับไฟล์ขนาดเล็กเท่านั้น):

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
เซิร์ฟเวอร์บางตัวจู้จี้จุกจิกมาก หากคุณมีปัญหาให้เพิ่มช่องว่างระหว่าง ";" และ "filename =" เมื่อสร้าง Content-Disposition และ "multipart / form-data; border =" + bound;
Kevin

ขอบคุณสำหรับความคิดเห็นของคุณ @Kevin อันที่จริงฉันไม่มีแอพเซิร์ฟเวอร์มากมายให้ตรวจสอบว่า :-)
BNK

@HemsLodha: ลองดูคำตอบของ RacZo ที่คำถามต่อไปนี้stackoverflow.com/questions/32240177/…เพื่อดูว่ามันช่วยคุณได้หรือไม่ :)
BNK

1
ฉันส่งคำขอโพสต์หลายส่วนพร้อมวอลเลย์และในที่สุดก็ตั้งค่าพารามิเตอร์เป็น url และใช้งานได้ ขอบคุณสำหรับความช่วยเหลือ @BNK
JosephM

@ user3561494 ไม่แนะนำสำหรับไฟล์ขนาดใหญ่เช่นวิดีโอ
BNK

9

กรอกคำขอหลายส่วนพร้อมความคืบหน้าในการอัปโหลด

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

    }
}

ตัวอย่างการใช้งาน

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_ - ฉันลองใช้รหัสของคุณ: แต่ฉันได้รับข้อผิดพลาดนี้: MultipartRequest.getBody: IOException กำลังเขียนถึง ByteArrayOutputStream คุณช่วยได้ไหม
Thiago

ฉันไม่มีรหัสของคุณฉันไม่สามารถแนะนำอะไรได้ดีไปกว่าการเปิดคำถามอื่นสาเหตุที่เป็นไปได้มากที่สุดอาจเป็นเพราะหน่วยความจำเสมือนของคุณหมด
AZ_

1
ฉันมีคำถามเปิดใหม่: stackoverflow.com/questions/31474585/…คุณช่วยได้ไหม ขอบคุณ
Thiago

ฉันจะเพิ่มพารามิเตอร์อื่น ๆ พร้อมกับรูปแบบหลายส่วนในคำขอเดียวได้อย่างไร
casillas

@casillas เพิ่มคู่ค่าคีย์ในส่วนหัวของคำขอ
AZ_

8

วิธีง่ายๆสำหรับ dev ที่ต้องการส่งพารามิเตอร์ POST ในคำขอหลายส่วน

ทำการเปลี่ยนแปลงต่อไปนี้ในคลาสซึ่งขยาย Request.java

ก่อนอื่นให้กำหนดค่าคงที่เหล่านี้:

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;

เพิ่มฟังก์ชันตัวช่วยเพื่อสร้างเนื้อหาโพสต์ให้คุณ:

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

แทนที่ getBody () และ getBodyContentType

public String getBodyContentType() {
    return MULTIPART_FORMDATA;
}

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

1
เรียบง่ายและทำงานได้อย่างสมบูรณ์แบบ! ฉันต้องไปsbPost.append("--" + BOUNDARY + "--");ก่อนที่จะกลับมาเนื่องจาก API ที่ฉันใช้ต้องมีแท็กปิด
ไซเรน

ขอบคุณ! สิ่งนี้ใช้ได้กับฉันในส่วนหน้า แต่ส่วนหลังโดยเฉพาะ nodejs มีปัญหาในการแยกวิเคราะห์หรือไม่ คุณเคยสัมผัสกับสิ่งนั้นหรือไม่?
Woppi

1
ขออภัยฉันไม่มีประสบการณ์แบ็กเอนด์ :(
Arpit Ratan

@Arpit ขอบคุณฉันสามารถแก้ปัญหาของฉันได้โดยใช้คลาสตัวช่วยนี้แทนgist.github.com/anggadarkprince/…
Woppi

ง่ายและมีประโยชน์ ขอบคุณสำหรับการแบ่งปันแนวทางนี้!
lgallard

4

คำตอบแรกเกี่ยวกับ SO

ฉันพบปัญหาเดียวกันและพบว่าโค้ดของ @alex มีประโยชน์มาก ฉันได้ทำการปรับเปลี่ยนง่ายๆเพื่อส่งผ่านพารามิเตอร์ให้มากที่สุดเท่าที่จำเป็นผ่าน HashMap และได้คัดลอกโดยทั่วไปparseNetworkResponse()จาก StringRequest ฉันได้ค้นหาทางออนไลน์และประหลาดใจมากที่พบว่างานทั่วไปเช่นนี้ไม่ค่อยได้รับคำตอบ อย่างไรก็ตามฉันหวังว่ารหัสจะช่วยได้:

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

และคุณสามารถใช้คลาสดังต่อไปนี้:

    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

อีกวิธีหนึ่งที่เบามากพร้อมประสิทธิภาพสูงพร้อมน้ำหนักบรรทุกขนาดใหญ่:

ไลบรารีไคลเอ็นต์ Http แบบอะซิงโครนัส 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!");

            };

        }

    );

}

2
ขนาดไฟล์ใหญ่เกินข้อยกเว้นหน่วยความจำ
ราวี

1
@Ravi +1 ไลบรารีนี้ใช้สำหรับการอัปโหลดไฟล์ขนาดใหญ่
Anton

หากคุณต้องการส่งไฟล์ขนาดใหญ่ให้ใช้ไลบรารี apache mime สำหรับหลายส่วนแล้วโพสต์โดยใช้ไลบรารีนี้
ราวี

คุณต้องบีบอัดขนาดของรูปภาพจากนั้นจึงใช้ไลบรารีนี้เพื่ออัปโหลดรูปภาพที่ฉันพบวิธีแก้ไขจาก URL ด้านล่าง: programmerguru.com/android-tutorial/… ฉันหวังว่านี่จะช่วยประหยัดเวลาของใครบางคนได้
Milan Sheth

@Ravi apache mime จะทำให้คุณมีข้อขัดแย้งและข้อผิดพลาดของคลาสที่ซ้ำกัน
Muhammad Saqib

0

นี่คือวิธีง่ายๆและตัวอย่างที่สมบูรณ์สำหรับการอัปโหลดไฟล์โดยใช้ Volley Android

1) การนำเข้า Gradle

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

2) ตอนนี้สร้าง 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) ตอนนี้สร้างคลาสเพื่อจัดการการร้องขอการอัปโหลดไฟล์ 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) และตอนนี้เรียกวิธีการเช่นนี้เพื่อเข้าชมบริการ

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

ฉันได้รับข้อผิดพลาดนี้จากโซลูชันของคุณ: java.lang.NoSuchMethodError: ไม่มีวิธีการโดยตรง <init> (ILjava / lang / String; Lcom / android / volley / Request $ Priority; Lcom / android / volley / Response $ ErrorListener; Lcom / android / volley / RetryPolicy;) V ในคลาส Lcom / android / volley / Request; หรือซุปเปอร์คลาส (คำประกาศของ 'com.android.volley.Request' ปรากฏใน /data/data/com.footballscout.app/files/instant-run/dex/slice-slice_4-classes.dex)
SpyZip

คุณตรวจสอบการไล่ระดับสีและการนำเข้าที่ถูกต้องในชั้นเรียนหรือไม่
เรียนด่วน

ฉันใช้ครั้งเดียวที่อธิบายไว้ที่นี่และคำตอบของคุณgithub.com/DWorkS/VolleyPlus
SpyZip

1
และตรวจสอบการนำเข้าของคุณในชั้นเรียนที่คุณเคยใช้เพราะคุณต้องไม่ใช้การนำเข้าที่ถูกต้องสำหรับวอลเลย์เช่นข้อผิดพลาดกำลังชี้ไปที่ตัวฟังข้อผิดพลาดตรวจสอบการนำเข้าอีกครั้งและไล่ระดับจากนั้นทำความสะอาดโครงการขอบคุณหากคุณยังพบสิ่งใด ๆ ปัญหาแจ้งให้เราทราบ
ผู้เรียนด่วน

-1

นี่คือวิธีการทำของฉัน อาจเป็นประโยชน์กับผู้อื่น:

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

 }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.