我正在尝试使用Volley发布我的图像,但我无法将我的图像上传到服务器。我总是得到 com.volley.ServerError 。当我使用Fiddler捕获图像上传请求时,它会给我一个500状态/错误代码。
以下是我从图库中选择图片的代码:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (data == null) {
Toast.makeText(getActivity(), "No Image Selected", Toast.LENGTH_SHORT).show();
} else if (requestCode == Constants.CHOICE_AVATAR_FROM_GALLERY && resultCode == getActivity().RESULT_OK) {
Bitmap avatar = getBitmapFromData(data);
RestClient restClient = new RestClient(getActivity());
restClient.stringRequest(Constants.PROFILE_IMAGE_UPDATE_REQUEST_ID, Constants.PROFILE_UPDATE_IMAGE_URL, this, imageData);
byte[] inputData=null;
try {
Bundle extra = data.getExtras();
Uri _uri= Uri.parse(extra.get("src_uri").toString());
InputStream iStream = getActivity().getContentResolver().openInputStream(_uri);
inputData = getBytes(iStream);
size = (inputData.length)/(1024*1024);
Log.d("ImageArray", "uri: " + size);
} catch (Exception e) {
e.printStackTrace();
}
if(size < 3){
imageView.setImageBitmap(avatar);
Map<String, String> dataMap = new HashMap<>();
dataMap.put("Email", Utility.sessionEmail(getActivity()));
restClient.multiPartImageUploadRequest(Constants.PROFILE_IMAGE_UPDATE_REQUEST_ID,
url, this, dataMap, inputData);
}else {
imageView.setImageResource(R.drawable.ic_camera);
Toast.makeText(getActivity(), "Image size shuld not exceed 3 MB", Toast.LENGTH_SHORT).show();
}
}
}
public String getPath(Uri uri) {
Cursor cursor = getActivity().getContentResolver().query(uri, null, null, null, null);
cursor.moveToFirst();
String document_id = cursor.getString(0);
document_id = document_id.substring(document_id.lastIndexOf(":") + 1);
cursor.close();
cursor = getActivity().getContentResolver().query(
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
null, MediaStore.Images.Media._ID + " = ? ", new String[]{document_id}, null);
cursor.moveToFirst();
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
cursor.close();
return path;
}
public void selectImage(View view) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(getCropIntent(intent), Constants.CHOICE_AVATAR_FROM_GALLERY);
}
private Intent getCropIntent(Intent intent) {
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 320);
intent.putExtra("outputY", 320);
intent.putExtra("return-data", true);
return intent;
}
public Bitmap getBitmapFromData(Intent data) {
Bitmap photo = null;
Uri photoUri = data.getData();
if (photoUri != null) {
photo = BitmapFactory.decodeFile(photoUri.getPath());
path = getPath(photoUri);
uri = photoUri;
Log.d("URI", "" + photoUri);
}
if (photo == null) {
Bundle extra = data.getExtras();
if (extra != null) {
photo = (Bitmap) extra.get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
photo.compress(Bitmap.CompressFormat.JPEG, 90, stream);
}
}
return photo;
}
这是处理上传 POST 请求
的方法 public void multiPartImageUploadRequest(int id, String url, ResponseHandler handler, Map<String, String> data, byte[] image){
int request_id = id;
final ResponseHandler responseHandler = handler;
final Map<String, String> params = data;
final byte[] imageData = image;
pDialog = new ProgressDialog(context);
pDialog.setMessage("Uploading Image...");
pDialog.show();
VolleyMultipartRequest request = new VolleyMultipartRequest(Request.Method.POST, url,
new Response.Listener<NetworkResponse>() {
@Override
public void onResponse(NetworkResponse response) {
String resultResponse = new String(response.data);
responseHandler.success(resultResponse, Constants.PROFILE_IMAGE_UPDATE_REQUEST_ID);
pDialog.dismiss();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
responseHandler.failure(error, Constants.PROFILE_IMAGE_UPDATE_REQUEST_ID);
pDialog.dismiss();
error.printStackTrace();
}
}) {
@Override
protected Map<String, String> getParams() {
return params;
}
@Override
protected Map<String, DataPart> getByteData() {
Map<String, DataPart> ImageParams = new HashMap<>();
// file name could found file base or direct access from real path
// for now just get bitmap data from ImageView
ImageParams.put("", new DataPart("file_avatar.jpg", imageData, "image/jpeg"));
return ImageParams;
}
};
request.setRetryPolicy(new DefaultRetryPolicy(30*1000, 1, 1.0f));
RestController.getInstance().addToRequestQueue(request);
}
VolleyMultipartRequest.java
public class VolleyMultipartRequest extends Request<NetworkResponse> {
private final String twoHyphens = "--";
private final String lineEnd = "\r\n";
private final String boundary = "apiclient-" + System.currentTimeMillis();
private Response.Listener<NetworkResponse> mListener;
private Response.ErrorListener mErrorListener;
private Map<String, String> mHeaders;
/**
* Default constructor with predefined header and post method.
*
* @param url request destination
* @param headers predefined custom header
* @param listener on success achieved 200 code from request
* @param errorListener on error http or library timeout
*/
public VolleyMultipartRequest(String url, Map<String, String> headers,
Response.Listener<NetworkResponse> listener,
Response.ErrorListener errorListener) {
super(Method.POST, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
this.mHeaders = headers;
}
/**
* Constructor with option method and default header configuration.
*
* @param method method for now accept POST and GET only
* @param url request destination
* @param listener on success event handler
* @param errorListener on error event handler
*/
public VolleyMultipartRequest(int method, String url,
Response.Listener<NetworkResponse> 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, 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;
}
/**
* Custom method handle data payload.
*
* @return Map data part label with data byte
* @throws AuthFailureError
*/
protected Map<String, DataPart> getByteData() throws AuthFailureError {
return null;
}
@Override
protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) {
try {
return Response.success(
response,
HttpHeaderParser.parseCacheHeaders(response));
} catch (Exception e) {
return Response.error(new ParseError(e));
}
}
@Override
protected void deliverResponse(NetworkResponse response) {
mListener.onResponse(response);
}
@Override
public void deliverError(VolleyError error) {
mErrorListener.onErrorResponse(error);
}
/**
* Parse string map into data output stream by key and value.
*
* @param dataOutputStream data output stream handle string parsing
* @param params string inputs collection
* @param encoding encode the inputs, default UTF-8
* @throws IOException
*/
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);
}
}
/**
* Parse data into data output stream.
*
* @param dataOutputStream data output stream handle file attachment
* @param data loop through data
* @throws IOException
*/
private void dataParse(DataOutputStream dataOutputStream, Map<String, DataPart> data) throws IOException {
for (Map.Entry<String, DataPart> entry : data.entrySet()) {
buildDataPart(dataOutputStream, entry.getValue(), entry.getKey());
}
}
/**
* Write string data into header and data output stream.
*
* @param dataOutputStream data output stream handle string parsing
* @param parameterName name of input
* @param parameterValue value of input
* @throws IOException
*/
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);
}
/**
* Write data file into header and data output stream.
*
* @param dataOutputStream data output stream handle data parsing
* @param dataFile data byte as DataPart from collection
* @param inputName name of data input
* @throws IOException
*/
private void buildDataPart(DataOutputStream dataOutputStream, DataPart dataFile, String inputName) throws IOException {
dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" +
inputName + "\"; filename=\"" + dataFile.getFileName() + "\"" + lineEnd);
if (dataFile.getType() != null && !dataFile.getType().trim().isEmpty()) {
dataOutputStream.writeBytes("Content-Type: " + dataFile.getType() + lineEnd);
Log.d("Name", inputName + " " + dataFile.getFileName());
}
dataOutputStream.writeBytes(lineEnd);
ByteArrayInputStream fileInputStream = new ByteArrayInputStream(dataFile.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);
}
/**
* Simple data container use for passing byte file
*/
public class DataPart {
private String fileName;
private byte[] content;
private String type;
/**
* Default data part
*/
public DataPart() {
}
/**
* Constructor with data.
*
* @param name label of data
* @param data byte data
*/
public DataPart(String name, byte[] data) {
fileName = name;
content = data;
}
/**
* Constructor with mime data type.
*
* @param name label of data
* @param data byte data
* @param mimeType mime data like "image/jpeg"
*/
public DataPart(String name, byte[] data, String mimeType) {
fileName = name;
content = data;
type = mimeType;
}
/**
* Getter file name.
*
* @return file name
*/
public String getFileName() {
return fileName;
}
/**
* Setter file name.
*
* @param fileName string file name
*/
public void setFileName(String fileName) {
this.fileName = fileName;
}
/**
* Getter content.
*
* @return byte file data
*/
public byte[] getContent() {
return content;
}
/**
* Setter content.
*
* @param content byte file data
*/
public void setContent(byte[] content) {
this.content = content;
}
/**
* Getter mime type.
*
* @return mime type
*/
public String getType() {
return type;
}
/**
* Setter mime type.
*
* @param type mime type
*/
public void setType(String type) {
this.type = type;
}
}
}
答案 0 :(得分:1)
因此,了解究竟是什么错误需要一些调试,我可以分享的是我的经验。
免责声明:我自己已经开始使用Retrofit2.0,并建议任何人都这样做,因为它更快,更容易使用,并且记录更多(并且已经记录好了) ),这是我之前与Volley的经历
在我的情况下,我需要将图像与文本字段一起上传,我构建的MultiPart类有点不同:
主要的是我使用MultiPartEntityBuilder
来简化内容,而类本身扩展Request<String>
,因为那是响应类型(通常更容易处理),所以这将是构造函数:
public MultiPartImageRequest(String url, String filePath, Response.Listener<String> listener, Response.ErrorListener errorListener)
{
super(Method.POST, url, errorListener);
this.listener = listener;
this.entityBuilder = MultipartEntityBuilder.create();
this.httpEntity = new MultipartEntity();
setShouldCache(false);
this.file = new File(filePath);
this.entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
this.entityBuilder.setBoundary(generateBoundary());
buildMultipartImageEntity();
}
自己创建边界,并使用包含文件的二进制体和我的文本字段构建实体,给出相应的内容类型,如下所示:
private void buildMultipartImageEntity()
{
try
{
ContentType contentType = ContentType.create("image/jpeg");
entityBuilder.addBinaryBody("userfile", file, contentType, file.getName());
entityBuilder.addTextBody("userid", String.valueOf(SettingsManager.getUserID()), ContentType.TEXT_PLAIN);
httpEntity = entityBuilder.build();
}
catch (Exception e)
{
VolleyLog.e("UnsupportedEncodingException");
}
}
private static final char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
private String generateBoundary()
{
StringBuilder buffer = new StringBuilder();
Random rand = new Random();
int count = rand.nextInt(11) + 30;
for (int i = 0; i < count; ++i)
{
buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
}
return buffer.toString();
}
从那里开始,getBodyContentType和getBody使用httpEntity对象:
@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();
}
我发现另一件事是需要在 utf-8 中解析响应,但在您的情况下可能不需要(取决于响应):
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response)
{
try
{
String responseBody = new String(response.data, "utf-8");
return (Response.success(responseBody, getCacheEntry()));
}
catch (UnsupportedEncodingException e)
{
VolleyLog.e("UnsupportedEncodingException");
return (Response.success("Uploaded, problem with url return", getCacheEntry()));
}
}
希望这有助于任何方式。