Volley 库、MultipartRequest 和 UTF-8 支持
Volley Library, MultipartRequest and UTF-8 Support
我有问题。
我写了一个有服务器端和客户端的应用程序。
客户可以通过应用程序发送包含文本和照片的报告。我使用了 volley 库;所以我为名为 MultipartRequest 的请求创建了自定义 class。
我遇到的问题是,当我发送一份带有图片和一些英文文本的报告时,该程序运行良好。但是当我想用波斯语发短信时,发不出去
我用 Postman 测试了服务器的 PHP classes,即使在波斯语中也能正常工作。
如果你能帮助我,我将不胜感激。
这是我的主要方法:
private void saveRapportToServer() {
final ProgressDialog progressDialog = new ProgressDialog(context);
progressDialog.setMessage("Sending...");
progressDialog.show();
MultipartRequest multipartRequest = new MultipartRequest(Request.Method.POST, AppConfig.URL_ADD_RAPPORT,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
progressDialog.dismiss();
try {
JSONObject jObj = new JSONObject(response);
if (!jObj.getBoolean("error")) {
// Rapport successfully stored in MySQL
} else {
Log.d(TAG, jObj.getString("error_msg"));
// there is some error
onError();
}
} catch (JSONException err) {
Log.d(TAG, err.toString());
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "Error on server --> " + error.toString());
progressDialog.dismiss();
// on error, storing the rapport to SQLite
onError();
}
}
) {
/**
* Here we are passing our string params
*/
@Override
protected Map<String, String> getParams() throws AuthFailureError {
// Posting params to add rapport url (php)
Map<String, String> params = new HashMap<>();
final String subject = _etSubject.getText().toString().trim();
final String location = _etLocation.getText().toString().trim();
final String description = _etDescrip.getText().toString().trim();
final HashMap<String, String> user = db.getUserDetails();
final String email = user.get("email");
params.put("email", email);
params.put("subject", subject);
params.put("location", location);
params.put("description", description);
return params;
}
/**
* Here we are passing image by renaming it with a unique name
*/
@Override
protected Map<String, DataPart> getByteData() throws AuthFailureError {
Map<String, DataPart> params = new HashMap<>();
long imageName = System.currentTimeMillis();
params.put("pic", new DataPart(imageName + ".jpg", getFileDataFromDrawable(bitmap)));
Log.i(TAG, "Data (image) params sent");
return params;
}
};
// adding request to request queue
AppController.getInstance().addToRequestQueue(multipartRequest, tag_req);
} // END: saveRapportToServer
/**
* The method is taking Bitmap as an argument
* this method will return the byte[] array for the given bitmap
* and we will send this array to the server
*/
public byte[] getFileDataFromDrawable(Bitmap bitmap) {
ByteArrayOutputStream baOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, baOutputStream);
return baOutputStream.toByteArray();
}
这里是我的 MultipartRequest class:
public class MultipartRequest extends Request<String> {
private final String twoHyphens = "--";
private final String lineEnd = "\r\n";
private final String boundary = "apiclient-" + System.currentTimeMillis();
private final Response.Listener<String> mListener;
private final Response.ErrorListener mErrorListener;
private final Map<String, String> mHeaders = new HashMap<>();
public MultipartRequest(int method, String url,
Response.Listener<String> 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 baOutputStream = new ByteArrayOutputStream();
DataOutputStream dOutputStream = new DataOutputStream(baOutputStream);
try {
// populate text payload
Map<String, String> params = getParams();
if(params != null && params.size() > 0) {
textParse(dOutputStream, params, getParamsEncoding());
}
// populate data byte payload
Map<String, DataPart> data = getByteData();
if(data != null && data.size() > 0) {
dataParse(dOutputStream, data);
}
// close multipart form data after text and file data
dOutputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
return baOutputStream.toByteArray();
} catch (IOException ex) {
ex.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<String> parseNetworkResponse(NetworkResponse response) {
try {
String utf8String = URLDecoder.decode(URLEncoder.encode(new String(response.data), "iso8859-1"), "UTF-8");
return Response.success(utf8String,
HttpHeaderParser.parseCacheHeaders(response));
} catch (Exception ex) {
return Response.error(new ParseError(ex));
}
}
@Override
protected void deliverResponse(String 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 ex) {
throw new RuntimeException("Encoding not supported: " + encoding, ex);
}
}
/**
* 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(lineEnd);
dataOutputStream.writeBytes(parameterValue + lineEnd);
}
/**
* Write data file into header and data output stream.
*
* @param dataOutputStream data output stream handle data parseing
* @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);
}
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);
}
class DataPart {
private String fileName;
private byte[] content;
private String type;
public DataPart() { }
public DataPart(String name, byte[] data) {
this.fileName = name;
this.content = data;
}
public String getFileName() {
return fileName;
}
public byte[] getContent() {
return content;
}
public String getType() {
return type;
}
}}
我知道这是一个老问题,但我在 Google 上找到了它,所以其他人也可以。无论如何,这是一个答案。这应该允许 Volley 发送带有以 UTF-8 编码的附加数据的多部分请求。它是从下面附上的 Oscar 的回答中复制的。
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); }
我有问题。
我写了一个有服务器端和客户端的应用程序。
客户可以通过应用程序发送包含文本和照片的报告。我使用了 volley 库;所以我为名为 MultipartRequest 的请求创建了自定义 class。
我遇到的问题是,当我发送一份带有图片和一些英文文本的报告时,该程序运行良好。但是当我想用波斯语发短信时,发不出去
我用 Postman 测试了服务器的 PHP classes,即使在波斯语中也能正常工作。
如果你能帮助我,我将不胜感激。
这是我的主要方法:
private void saveRapportToServer() {
final ProgressDialog progressDialog = new ProgressDialog(context);
progressDialog.setMessage("Sending...");
progressDialog.show();
MultipartRequest multipartRequest = new MultipartRequest(Request.Method.POST, AppConfig.URL_ADD_RAPPORT,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
progressDialog.dismiss();
try {
JSONObject jObj = new JSONObject(response);
if (!jObj.getBoolean("error")) {
// Rapport successfully stored in MySQL
} else {
Log.d(TAG, jObj.getString("error_msg"));
// there is some error
onError();
}
} catch (JSONException err) {
Log.d(TAG, err.toString());
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "Error on server --> " + error.toString());
progressDialog.dismiss();
// on error, storing the rapport to SQLite
onError();
}
}
) {
/**
* Here we are passing our string params
*/
@Override
protected Map<String, String> getParams() throws AuthFailureError {
// Posting params to add rapport url (php)
Map<String, String> params = new HashMap<>();
final String subject = _etSubject.getText().toString().trim();
final String location = _etLocation.getText().toString().trim();
final String description = _etDescrip.getText().toString().trim();
final HashMap<String, String> user = db.getUserDetails();
final String email = user.get("email");
params.put("email", email);
params.put("subject", subject);
params.put("location", location);
params.put("description", description);
return params;
}
/**
* Here we are passing image by renaming it with a unique name
*/
@Override
protected Map<String, DataPart> getByteData() throws AuthFailureError {
Map<String, DataPart> params = new HashMap<>();
long imageName = System.currentTimeMillis();
params.put("pic", new DataPart(imageName + ".jpg", getFileDataFromDrawable(bitmap)));
Log.i(TAG, "Data (image) params sent");
return params;
}
};
// adding request to request queue
AppController.getInstance().addToRequestQueue(multipartRequest, tag_req);
} // END: saveRapportToServer
/**
* The method is taking Bitmap as an argument
* this method will return the byte[] array for the given bitmap
* and we will send this array to the server
*/
public byte[] getFileDataFromDrawable(Bitmap bitmap) {
ByteArrayOutputStream baOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, baOutputStream);
return baOutputStream.toByteArray();
}
这里是我的 MultipartRequest class:
public class MultipartRequest extends Request<String> {
private final String twoHyphens = "--";
private final String lineEnd = "\r\n";
private final String boundary = "apiclient-" + System.currentTimeMillis();
private final Response.Listener<String> mListener;
private final Response.ErrorListener mErrorListener;
private final Map<String, String> mHeaders = new HashMap<>();
public MultipartRequest(int method, String url,
Response.Listener<String> 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 baOutputStream = new ByteArrayOutputStream();
DataOutputStream dOutputStream = new DataOutputStream(baOutputStream);
try {
// populate text payload
Map<String, String> params = getParams();
if(params != null && params.size() > 0) {
textParse(dOutputStream, params, getParamsEncoding());
}
// populate data byte payload
Map<String, DataPart> data = getByteData();
if(data != null && data.size() > 0) {
dataParse(dOutputStream, data);
}
// close multipart form data after text and file data
dOutputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
return baOutputStream.toByteArray();
} catch (IOException ex) {
ex.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<String> parseNetworkResponse(NetworkResponse response) {
try {
String utf8String = URLDecoder.decode(URLEncoder.encode(new String(response.data), "iso8859-1"), "UTF-8");
return Response.success(utf8String,
HttpHeaderParser.parseCacheHeaders(response));
} catch (Exception ex) {
return Response.error(new ParseError(ex));
}
}
@Override
protected void deliverResponse(String 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 ex) {
throw new RuntimeException("Encoding not supported: " + encoding, ex);
}
}
/**
* 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(lineEnd);
dataOutputStream.writeBytes(parameterValue + lineEnd);
}
/**
* Write data file into header and data output stream.
*
* @param dataOutputStream data output stream handle data parseing
* @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);
}
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);
}
class DataPart {
private String fileName;
private byte[] content;
private String type;
public DataPart() { }
public DataPart(String name, byte[] data) {
this.fileName = name;
this.content = data;
}
public String getFileName() {
return fileName;
}
public byte[] getContent() {
return content;
}
public String getType() {
return type;
}
}}
我知道这是一个老问题,但我在 Google 上找到了它,所以其他人也可以。无论如何,这是一个答案。这应该允许 Volley 发送带有以 UTF-8 编码的附加数据的多部分请求。它是从下面附上的 Oscar 的回答中复制的。
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); }