Java 使用 Volley 和不使用 HttpEntity 处理 POST 多部分请求
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32240177/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Working POST Multipart Request with Volley and without HttpEntity
提问by BNK
This is not really a question, however, I would like to share some of my working code here for your reference when you need.
这不是一个真正的问题,但是,我想在这里分享我的一些工作代码,供您在需要时参考。
As we know that HttpEntity
is deprecated from API22 and comletely removed since API23. At the moment, we cannot access HttpEntity Reference on Android Developeranymore (404). So, the following is my working sample code for POST Multipart Request with Volley and without HttpEntity. It's working, tested with Asp.Net Web API
. Of course, the code perhaps is just a basic sample that posts two existed drawable files, also is not the best solution for all cases, and not good tuning.
正如我们所知,HttpEntity
它已从 API22 中弃用,并自 API23 起完全删除。目前,我们无法再访问Android Developer 上的 HttpEntity Reference(404)。因此,以下是我使用 Volley 且不使用 HttpEntity 的 POST Multipart Request 的工作示例代码。它正在工作,经过测试Asp.Net Web API
。当然,代码可能只是一个基本示例,发布了两个现有的可绘制文件,也不是所有情况下的最佳解决方案,也不是很好的调优。
MultipartActivity.java:
多部分活动.java:
package com.example.multipartvolley;
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 android.widget.Toast;
import com.android.volley.NetworkResponse;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class MultipartActivity extends Activity {
private final Context context = this;
private final String twoHyphens = "--";
private final String lineEnd = "\r\n";
private final String boundary = "apiclient-" + System.currentTimeMillis();
private final String mimeType = "multipart/form-data;boundary=" + boundary;
private byte[] multipartBody;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_multipart);
byte[] fileData1 = getFileDataFromDrawable(context, R.drawable.ic_action_android);
byte[] fileData2 = getFileDataFromDrawable(context, R.drawable.ic_action_book);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
try {
// the first file
buildPart(dos, fileData1, "ic_action_android.png");
// the second file
buildPart(dos, fileData2, "ic_action_book.png");
// send multipart form data necesssary after file data
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
// pass to multipart body
multipartBody = bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
String url = "http://192.168.1.100/api/postfile";
MultipartRequest multipartRequest = new MultipartRequest(url, null, mimeType, multipartBody, new Response.Listener<NetworkResponse>() {
@Override
public void onResponse(NetworkResponse response) {
Toast.makeText(context, "Upload successfully!", Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(context, "Upload failed!\r\n" + error.toString(), Toast.LENGTH_SHORT).show();
}
});
VolleySingleton.getInstance(context).addToRequestQueue(multipartRequest);
}
@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);
}
private void buildPart(DataOutputStream dataOutputStream, byte[] fileData, String fileName) throws IOException {
dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"uploaded_file\"; filename=\""
+ fileName + "\"" + lineEnd);
dataOutputStream.writeBytes(lineEnd);
ByteArrayInputStream fileInputStream = new ByteArrayInputStream(fileData);
int bytesAvailable = fileInputStream.available();
int maxBufferSize = 1024 * 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
byte[] buffer = new byte[bufferSize];
// read file and write it into form...
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);
}
private byte[] getFileDataFromDrawable(Context context, int id) {
Drawable drawable = ContextCompat.getDrawable(context, id);
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 0, byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
}
}
MultipartRequest.java:
MultipartRequest.java:
package com.example.multipartvolley;
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 java.util.Map;
class MultipartRequest extends Request<NetworkResponse> {
private final Response.Listener<NetworkResponse> mListener;
private final Response.ErrorListener mErrorListener;
private final Map<String, String> mHeaders;
private final String mMimeType;
private final byte[] mMultipartBody;
public MultipartRequest(String url, Map<String, String> headers, String mimeType, byte[] multipartBody, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) {
super(Method.POST, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
this.mHeaders = headers;
this.mMimeType = mimeType;
this.mMultipartBody = multipartBody;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return (mHeaders != null) ? mHeaders : super.getHeaders();
}
@Override
public String getBodyContentType() {
return mMimeType;
}
@Override
public byte[] getBody() throws AuthFailureError {
return mMultipartBody;
}
@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);
}
}
UPDATE:
更新:
For text part, please refer to @Oscar's answer below.
对于文本部分,请参阅下面@Oscar 的回答。
回答by Oscar Salguero
Just want to add to the answer. I was trying to figure how to append text fields to the body and created the following function to do it:
只是想添加到答案中。我试图弄清楚如何将文本字段附加到正文并创建了以下函数来做到这一点:
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);
}
It is working pretty well.
它工作得很好。
回答by Angga Ari Wijaya
I rewrite your code @RacZo and @BNK more modular and easy to use like
我重写了你的代码 @RacZo 和 @BNK,更加模块化和易于使用,例如
VolleyMultipartRequest multipartRequest = new VolleyMultipartRequest(Request.Method.POST, url, new Response.Listener<NetworkResponse>() {
@Override
public void onResponse(NetworkResponse response) {
String resultResponse = new String(response.data);
// parse success output
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("api_token", "gh659gjhvdyudo973823tt9gvjf7i6ric75r76");
params.put("name", "Angga");
params.put("location", "Indonesia");
params.put("about", "UI/UX Designer");
params.put("contact", "[email protected]");
return params;
}
@Override
protected Map<String, DataPart> getByteData() {
Map<String, DataPart> params = new HashMap<>();
// file name could found file base or direct access from real path
// for now just get bitmap data from ImageView
params.put("avatar", new DataPart("file_avatar.jpg", AppHelper.getFileDataFromDrawable(getBaseContext(), mAvatarImage.getDrawable()), "image/jpeg"));
params.put("cover", new DataPart("file_cover.jpg", AppHelper.getFileDataFromDrawable(getBaseContext(), mCoverImage.getDrawable()), "image/jpeg"));
return params;
}
};
VolleySingleton.getInstance(getBaseContext()).addToRequestQueue(multipartRequest);
Check full of code VolleyMultipartRequest
at my gist.
VolleyMultipartRequest
在我的要点检查完整的代码。
回答by Sepehr Nozaryian
For those who are struggling to send utf-8 parameters and still no luck, the problem I had was in the dataOutputStream, and change the code of @RacZo to below code:
对于那些正在努力发送 utf-8 参数但仍然没有运气的人,我遇到的问题是在 dataOutputStream 中,并将@RacZo 的代码更改为以下代码:
private void buildTextPart(DataOutputStream dataOutputStream, String parameterName, String parameterValue) throws IOException {
dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"");
dataOutputStream.write(parameterName.getBytes("UTF-8"));
dataOutputStream.writeBytes(lineEnd);
dataOutputStream.writeBytes("Content-Type: text/plain; charset=UTF-8" + lineEnd);
dataOutputStream.writeBytes(lineEnd);
dataOutputStream.write(parameterValue.getBytes("UTF-8"));
dataOutputStream.writeBytes(lineEnd);
}
回答by Reaz Murshed
I found a wrapper of the original volley library which is easier to integrate for multi-part requests. It also supports uploading the multi-part data along with other request parameters. Hence I am sharing my code for the future developers who might run into the problem that I was having (i.e. uploading multi-part data using volley along with some other parameters).
我找到了原始 volley 库的包装器,它更容易集成多部分请求。它还支持上传多部分数据以及其他请求参数。因此,我正在为未来的开发人员分享我的代码,他们可能会遇到我遇到的问题(即使用 volley 上传多部分数据以及其他一些参数)。
Add the following library in the build.gradle
file.
在build.gradle
文件中添加以下库。
dependencies {
compile 'dev.dworks.libs:volleyplus:+'
}
Please note that, I removed the original volley library from my build.gradle
and used the above library instead which can handle both multi-part and normal requests having similar integration technique.
请注意,我从我的中删除了原始的 volley 库build.gradle
并使用了上面的库,它可以处理具有类似集成技术的多部分和普通请求。
Then I just had to write the following class which handles the POST request operation.
然后我只需要编写以下处理 POST 请求操作的类。
public class POSTMediasTask {
public void uploadMedia(final Context context, String filePath) {
String url = getUrlForPOSTMedia(); // This is a dummy function which returns the POST url for you
SimpleMultiPartRequest multiPartRequestWithParams = new SimpleMultiPartRequest(Request.Method.POST, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("Response", response);
// TODO: Do something on success
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// TODO: Handle your error here
}
});
// Add the file here
multiPartRequestWithParams.addFile("file", filePath);
// Add the params here
multiPartRequestWithParams.addStringParam("param1", "SomeParamValue1");
multiPartRequestWithParams.addStringParam("param2", "SomeParamValue2");
RequestQueue queue = Volley.newRequestQueue(context);
queue.add(multiPartRequestWithParams);
}
}
Now execute the task like the following.
现在执行如下任务。
new POSTMediasTask().uploadMedia(context, mediaPath);
You can upload one file at a time using this library. However, I could manage to upload multiple files, just by initiating multiple tasks.
您可以使用此库一次上传一个文件。但是,我可以通过启动多个任务来上传多个文件。
Hope that helps!
希望有帮助!
回答by Ika
Here is a Kotlin version of a class allowing multipart request with Volley 1.1.1.
这是一个 Kotlin 版本的类,允许使用 Volley 1.1.1 进行多部分请求。
It's mostly based on @BNK's solution but slighly simplified. I did not notice any particular performance issue. I uploaded a 5Mb pic in about 3 seconds.
它主要基于@BNK 的解决方案,但略有简化。我没有注意到任何特定的性能问题。我在大约 3 秒内上传了一张 5Mb 的图片。
class MultipartWebservice(context: Context) {
private var queue: RequestQueue? = null
private val boundary = "apiclient-" + System.currentTimeMillis()
private val mimeType = "multipart/form-data;boundary=$boundary"
init {
queue = Volley.newRequestQueue(context)
}
fun sendMultipartRequest(
method: Int,
url: String,
fileData: ByteArray,
fileName: String,
listener: Response.Listener<NetworkResponse>,
errorListener: Response.ErrorListener
) {
// Create multi part byte array
val bos = ByteArrayOutputStream()
val dos = DataOutputStream(bos)
buildMultipartContent(dos, fileData, fileName)
val multipartBody = bos.toByteArray()
// Request header, if needed
val headers = HashMap<String, String>()
headers["API-TOKEN"] = "458e126682d577c97d225bbd73a75b5989f65e977b6d8d4b2267537019ad9d20"
val request = MultipartRequest(
method,
url,
errorListener,
listener,
headers,
mimeType,
multipartBody
)
queue?.add(request)
}
@Throws(IOException::class)
private fun buildMultipartContent(dos: DataOutputStream, fileData: ByteArray, fileName: String) {
val twoHyphens = "--"
val lineEnd = "\r\n"
dos.writeBytes(twoHyphens + boundary + lineEnd)
dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"$fileName\"$lineEnd")
dos.writeBytes(lineEnd)
dos.write(fileData)
dos.writeBytes(lineEnd)
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd)
}
class MultipartRequest(
method: Int,
url: String,
errorListener: Response.ErrorListener?,
private var listener: Response.Listener<NetworkResponse>,
private var headers: MutableMap<String, String>,
private var mimeType: String,
private var multipartBody: ByteArray
) : Request<NetworkResponse>(method, url, errorListener) {
override fun getHeaders(): MutableMap<String, String> {
return if (headers.isEmpty()) super.getHeaders() else headers
}
override fun getBodyContentType(): String {
return mimeType
}
override fun getBody(): ByteArray {
return multipartBody
}
override fun parseNetworkResponse(response: NetworkResponse?): Response<NetworkResponse> {
return try {
Response.success(response, HttpHeaderParser.parseCacheHeaders(response))
} catch (e: Exception) {
Response.error(ParseError(e))
}
}
override fun deliverResponse(response: NetworkResponse?) {
listener.onResponse(response)
}
}
}