使用Android Volley进行多部分请求(将多个图像上传到服务器)

时间:2017-09-26 15:20:28

标签: android android-volley uri jsonobjectrequest

这是我要求将图像上传到服务器https://gist.github.com/anggadarkprince/a7c536da091f4b26bb4abf2f92926594

的要点

但我使用TedPicker OnMultiImageSelectedListener来选择多个图像https://android-arsenal.com/details/1/4320

它在ArrayLIst中给出了selectedUriList。我将所有的Uri转换为byteArray并上传到服务器。 这是我的上传图像代码。

private void imageUpload() {

    final ProgressDialog loading = ProgressDialog.show(this,"Uploading...","Please wait...",false,false);
    VolleyMultipartRequest multipartRequest = new VolleyMultipartRequest(Request.Method.POST, Constants.UPLOAD_URL, new Response.Listener<NetworkResponse>() {
        @Override
        public void onResponse(NetworkResponse response) {
            String resultResponse = new String(response.data);
            try {
                JSONObject result = new JSONObject(resultResponse);
                String status = result.getString("status");
                String message = result.getString("message");

                loading.dismiss();

                if (status.equals(Constants.REQUEST_SUCCESS)) {
                    // tell everybody you have succed upload image and post strings
                    Log.i("Messsage", message);
                } else {
                    Log.i("Unexpected", message);
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            loading.dismiss();
            NetworkResponse networkResponse = error.networkResponse;
            String errorMessage = "Unknown error";
            if (networkResponse == null) {
                if (error.getClass().equals(TimeoutError.class)) {
                    errorMessage = "Request timeout";
                } else if (error.getClass().equals(NoConnectionError.class)) {
                    errorMessage = "Failed to connect server";
                }
            } else {
                String result = new String(networkResponse.data);
                try {
                    JSONObject response = new JSONObject(result);
                    String status = response.getString("status");
                    String message = response.getString("message");

                    Log.e("Error Status", status);
                    Log.e("Error Message", message);

                    if (networkResponse.statusCode == 404) {
                        errorMessage = "Resource not found";
                    } else if (networkResponse.statusCode == 401) {
                        errorMessage = message+" Please login again";
                    } else if (networkResponse.statusCode == 400) {
                        errorMessage = message+ " Check your inputs";
                    } else if (networkResponse.statusCode == 500) {
                        errorMessage = message+" Something is getting wrong";
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
            Log.i("Error", errorMessage);
        }
    }) {
        @Override
        protected Map<String, String> getParams() {
            Map<String, String> params = new HashMap<>();
            params.put("folder_name", FOLDER_NAME);
            return params;
        }

        @Override
        protected Map<String, DataPart> getByteData() {
            Map<String, DataPart> params = new HashMap<>();

            int i = 0;
            for (Uri uri : selectedUriList) {
                i++;
                try {
                    InputStream iStream = getContentResolver().openInputStream(uri);
                    byte[] inputData = getBytes(iStream);
                    params.put("image_file"+i, new DataPart("image"+i+".jpg", inputData , "image/jpeg"));

                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

            return params;
        }
    };

    VolleySingleton.getInstance(getBaseContext()).addToRequestQueue(multipartRequest);
}

public byte[] getBytes(InputStream inputStream) throws IOException {
    ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
    int bufferSize = 1024;
    byte[] buffer = new byte[bufferSize];

    int len = 0;
    while ((len = inputStream.read(buffer)) != -1) {
        byteBuffer.write(buffer, 0, len);
    }
    return byteBuffer.toByteArray();
}

尝试上传图片时面临的错误

I/System.out: [socket]rx timeout:2500
I/System.out: [socket][0] connection ascentcity.com/119.81.195.196:80;LocalPort=45296(2500)
I/System.out: [CDS]connect[ascentcity.com/119.81.195.196:80] tm:2
D/Posix: [Posix_connect Debug]Process com.vst.image.vehiclestimageclassifier :80 
I/System.out: [socket][/192.168.31.42:45296] connected
I/System.out: [socket]rx timeout:2500
D/Volley: [22832] BasicNetwork.logSlowRequests: HTTP response for request=<[ ] http://ascentcity.com/Mobileapp/upload.php 0x43980958 NORMAL 1> [lifetime=48143], [size=116], [rc=200], [retryCount=0]
W/System.err: org.json.JSONException: Value Sorry of type java.lang.String cannot be converted to JSONObject
W/System.err:     at org.json.JSON.typeMismatch(JSON.java:111)
W/System.err:     at org.json.JSONObject.<init>(JSONObject.java:160)
W/System.err:     at org.json.JSONObject.<init>(JSONObject.java:173)
W/System.err:     at com.vst.image.vehiclestimageclassifier.MainActivity$3.onResponse(MainActivity.java:171)
W/System.err:     at com.vst.image.vehiclestimageclassifier.MainActivity$3.onResponse(MainActivity.java:166)
W/System.err:     at com.vst.image.vehiclestimageclassifier.VolleyMultipartRequest.deliverResponse(VolleyMultipartRequest.java:127)
W/System.err:     at com.vst.image.vehiclestimageclassifier.VolleyMultipartRequest.deliverResponse(VolleyMultipartRequest.java:24)
W/System.err:     at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ExecutorDelivery.java:99)
W/System.err:     at android.os.Handler.handleCallback(Handler.java:739)
W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err:     at android.os.Looper.loop(Looper.java:157)
W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5429)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

我有几个问题

  1. 是否可以从这样的图像中检索字节数据,还是有更好的方法?

        @Override
        protected Map<String, DataPart> getByteData() {
            Map<String, DataPart> params = new HashMap<>();
    
            int i = 0;
            for (Uri uri : selectedUriList) {
                i++;
                try {
                    InputStream iStream = getContentResolver().openInputStream(uri);
                    byte[] inputData = getBytes(iStream);
                    // file name could found file base or direct access from real path
                    // for now just get bitmap data from ImageView
                    params.put("image_file"+i, new DataPart("image"+i+".jpg", inputData , "image/jpeg"));
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
            }
    
            return params;
        }
    
  2. iam面临的错误是由于服务器速度慢?或者是其他东西 ?如何调试?

  3. 我需要与后端团队(服务器端)沟通,我可以处理我的请求。

  4. 以下是完成项目代码https://github.com/sooorajjj/vstImageClassifier的github链接 谢谢!

2 个答案:

答案 0 :(得分:0)

这是我自定义的 VolleyMultipartRequest 文件。您可以直接使用它上传具有相同参数名称的多张图片:

import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.HttpHeaderParser;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Map;

/**
 * Custom request to make multipart header and upload file.
 * <p>
 * Sketch Project Studio
 * Created by bhuvnesh 8/10/2019
*/
@SuppressWarnings("unused")
public class VolleyMultipartRequest extends Request<JSONObject> {
  private final String twoHyphens = "--";
  private final String lineEnd = "\r\n";
  private final String boundary = "apiclient-" + System.currentTimeMillis();

  private Response.Listener<JSONObject> mListener;
  private Response.ErrorListener mErrorListener;
  private Map<String, String> mHeaders;


public VolleyMultipartRequest(String url, Map<String, String> headers,
                              Response.Listener<JSONObject> listener,
                              Response.ErrorListener errorListener) {
    super(Method.POST, url, errorListener);
    this.mListener = listener;
    this.mErrorListener = errorListener;
    this.mHeaders = headers;
}

public VolleyMultipartRequest(int method, String url,
                              Response.Listener<JSONObject> listener,
                              Response.ErrorListener errorListener) {
    super(method, url, errorListener);
    this.mListener = listener;
    this.mErrorListener = errorListener;
}

@Override
public Map<String, String> getHeaders() throws AuthFailureError {
    return (mHeaders != null) ? mHeaders : super.getHeaders();
}

@Override
public String getBodyContentType() {
    return "multipart/form-data;boundary=" + boundary;
}

@Override
public byte[] getBody() throws AuthFailureError {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    DataOutputStream dos = new DataOutputStream(bos);

    try {
        // populate text payload
        Map<String, String> params = getParams();
        if (params != null && params.size() > 0) {
            textParse(dos, params, getParamsEncoding());
        }

        // populate data byte payload
        Map<String, ArrayList<DataPart>> data = getByteData();
        if (data != null && data.size() > 0) {
            dataParse(dos, data);
        }

        // close multipart form data after text and file data
        dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

        return bos.toByteArray();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}


protected Map<String, ArrayList<DataPart>> getByteData() throws AuthFailureError {
    return null;
}

@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
    try {
        String jsonString = new String(response.data,
                HttpHeaderParser.parseCharset(response.headers));
        return Response.success(new JSONObject(jsonString),
                HttpHeaderParser.parseCacheHeaders(response));
    } catch (UnsupportedEncodingException e) {
        return Response.error(new ParseError(e));
    } catch (JSONException je) {
        return Response.error(new ParseError(je));
    }
}

@Override
protected void deliverResponse(JSONObject response) {
    mListener.onResponse(response);
}

@Override
public void deliverError(VolleyError error) {
    mErrorListener.onErrorResponse(error);
}

private void textParse(DataOutputStream dataOutputStream, Map<String, String> params, String encoding) throws IOException {
    try {
        for (Map.Entry<String, String> entry : params.entrySet()) {
            buildTextPart(dataOutputStream, entry.getKey(), entry.getValue());
        }
    } catch (UnsupportedEncodingException uee) {
        throw new RuntimeException("Encoding not supported: " + encoding, uee);
    }
}

private void dataParse(DataOutputStream dataOutputStream, Map<String, ArrayList<DataPart>> data) throws IOException {
    for (Map.Entry<String, ArrayList<DataPart>> entry : data.entrySet()) {
        buildDataPart(dataOutputStream, entry.getValue(), entry.getKey());
    }
}

private void buildTextPart(DataOutputStream dataOutputStream, String parameterName, String parameterValue) throws IOException {
    dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
    dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" + parameterName + "\"" + lineEnd);
    //dataOutputStream.writeBytes("Content-Type: text/plain; charset=UTF-8" + lineEnd);
    dataOutputStream.writeBytes(lineEnd);
    dataOutputStream.writeBytes(parameterValue + lineEnd);
}

private void buildDataPart(DataOutputStream dataOutputStream, ArrayList<DataPart> dataFile, String inputName) throws IOException {
    for (int i=0; i<dataFile.size(); i++){
        dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
        DataPart dp = dataFile.get(i);
        dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" +
                inputName + "\"; filename=\"" + dp.getFileName() + "\"" + lineEnd);
        if (dp.getType() != null && !dp.getType().trim().isEmpty()) {
            dataOutputStream.writeBytes("Content-Type: " + dp.getType() + lineEnd);
        }
        dataOutputStream.writeBytes(lineEnd);

        ByteArrayInputStream fileInputStream = new ByteArrayInputStream(dp.getContent());
        int bytesAvailable = fileInputStream.available();

        int maxBufferSize = 1024 * 1024;
        int bufferSize = Math.min(bytesAvailable, maxBufferSize);
        byte[] buffer = new byte[bufferSize];

        int bytesRead = fileInputStream.read(buffer, 0, bufferSize);

        while (bytesRead > 0) {
            dataOutputStream.write(buffer, 0, bufferSize);
            bytesAvailable = fileInputStream.available();
            bufferSize = Math.min(bytesAvailable, maxBufferSize);
            bytesRead = fileInputStream.read(buffer, 0, bufferSize);
        }

        dataOutputStream.writeBytes(lineEnd);
    }
}

public class DataPart {
    private String fileName;
    private byte[] content;
    private String type;

    public DataPart() {
    }

    public DataPart(String name, byte[] data) {
        fileName = name;
        content = data;
    }

    public DataPart(String name, byte[] data, String mimeType) {
        fileName = name;
        content = data;
        type = mimeType;
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public byte[] getContent() {
        return content;
    }

    public void setContent(byte[] content) {
        this.content = content;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}

}

这是使用此volleyMultipartRequest文件的代码:

 VolleyMultipartRequest multipartRequest = new VolleyMultipartRequest(Request.Method.POST,
            BaseURL.ADD_INVOICE, new Response.Listener<JSONObject>() {

        @Override
        public void onResponse(JSONObject response) {

            try {
                Toast.makeText(HomeActivity.this, response.getString("message"), Toast.LENGTH_LONG).show();
                if (response.getString("message").equals("Invoice added successfully!!")){
                    check=true;
                    Intent intent = new Intent(HomeActivity.this, ThankyouActivity.class);
                    startActivity(intent);
                    //finish();

                }else {
                    Toast.makeText(HomeActivity.this, response.getString("message"), Toast.LENGTH_LONG).show();
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            progressdialog.dismisswaitdialog();
            if (error instanceof TimeoutError || error instanceof NoConnectionError) {
                Toast.makeText(HomeActivity.this, getResources().getString(R.string.connection_time_out), Toast.LENGTH_SHORT).show();
            }
        }
    }) {
        @Override
        protected Map<String, String> getParams() {
            Map<String, String> params = new HashMap<>();
            params.put("user_id", user_id);
            params.put("year", et_year.getText().toString());
            params.put("make", et_make.getText().toString());
            params.put("model", et_model.getText().toString());
            return params;
        }

        @Override
        protected Map<String, ArrayList<DataPart>> getByteData() {
            Map<String, VolleyMultipartRequest.DataPart> params = new HashMap<>();
            Map<String, ArrayList<DataPart>> imageList = new HashMap<>();
            ArrayList<DataPart> dataPart = new ArrayList<>();
            String imagename = "image[]";
            for (int i=0; i<encodedImageList.size(); i++){
                VolleyMultipartRequest.DataPart dp = new VolleyMultipartRequest.DataPart(imagename+i, Base64.decode(encodedImageList.get(i), Base64.DEFAULT), "image/jpeg");
                dataPart.add(dp);
                //params.put(imagename, new DataPart(imagename+i, Base64.decode(encodedImageList.get(i), Base64.DEFAULT), "image/jpeg"));
            }
            imageList.put("image[]", dataPart);

            return imageList;
        }
    };
    int socketTimeout = 500000;//30 seconds - change to what you want
    RetryPolicy policy = new DefaultRetryPolicy(socketTimeout, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
    multipartRequest.setRetryPolicy(policy);
    AppController.getInstance().addToRequestQueue(multipartRequest);

答案 1 :(得分:0)

对于 Kotlin,使用 MutableMap<String,ArrayList<DataPart>> 而不是 Map<String,ArrayList<DataPart>>

并且应该像这样初始化。

val dataParts:MutableMap<String,ArrayList<DataPart>> = mutableMapOf()

并将数据与HashMap<K,V>相同。