使用Android SDK发布多部分请求

我试图做一些我认为会相对简单的事情:使用Android SDK将图像上传到服务器。 我发现了很多示例代码:

http://groups.google.com/group/android-developers/browse_thread/thread/f9e17bbaf50c5fc/46145fcacd450e48

http://linklens.blogspot.com/2009/06/android-multipart-upload.html

但是,这两个都不适合我。 我不断遇到的困惑是多部分请求真正需要的。 Android的多部分上传(带图片) 最简单的方法是什么?

任何帮助或建议将不胜感激!

2014年4月29日更新:

我的回答现在已经很老了,我猜你宁愿使用某种高级库,例如Retrofit 。


基于这个博客,我想出了以下解决方案: http : //blog.tacticalnuclearstrike.com/2010/01/using-multipartentity-in-android-applications/

您将不得不下载额外的库来运行MultipartEntity

1)从http://james.apache.org/download.cgi#Apache_Mime4J下载httpcomponents-client-4.1.zip并将apache-mime4j-0.6.1.jar添加到您的项目中。

2)从http://hc.apache.org/downloads.cgi下载httpcomponents-client-4.1-bin.zip,并将httpclient-4.1.jar,httpcore-4.1.jar和httpmime-4.1.jar添加到您的项目中。

3)使用下面的示例代码。

 private DefaultHttpClient mHttpClient; public ServerCommunication() { HttpParams params = new BasicHttpParams(); params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1); mHttpClient = new DefaultHttpClient(params); } public void uploadUserPhoto(File image) { try { HttpPost httppost = new HttpPost("some url"); MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); multipartEntity.addPart("Title", new StringBody("Title")); multipartEntity.addPart("Nick", new StringBody("Nick")); multipartEntity.addPart("Email", new StringBody("Email")); multipartEntity.addPart("Description", new StringBody(Settings.SHARE.TEXT)); multipartEntity.addPart("Image", new FileBody(image)); httppost.setEntity(multipartEntity); mHttpClient.execute(httppost, new PhotoUploadResponseHandler()); } catch (Exception e) { Log.e(ServerCommunication.class.getName(), e.getLocalizedMessage(), e); } } private class PhotoUploadResponseHandler implements ResponseHandler { @Override public Object handleResponse(HttpResponse response) throws ClientProtocolException, IOException { HttpEntity r_entity = response.getEntity(); String responseString = EntityUtils.toString(r_entity); Log.d("UPLOAD", responseString); return null; } } 

由于MultiPartEntity已被弃用 。 所以这里有新的做法! 你只需要httpcore.jar(latest)httpmime.jar(latest)从Apache站点下载它们。

 try { HttpClient client = new DefaultHttpClient(); HttpPost post = new HttpPost(URL); MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create(); entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); entityBuilder.addTextBody(USER_ID, userId); entityBuilder.addTextBody(NAME, name); entityBuilder.addTextBody(TYPE, type); entityBuilder.addTextBody(COMMENT, comment); entityBuilder.addTextBody(LATITUDE, String.valueOf(User.Latitude)); entityBuilder.addTextBody(LONGITUDE, String.valueOf(User.Longitude)); if(file != null) { entityBuilder.addBinaryBody(IMAGE, file); } HttpEntity entity = entityBuilder.build(); post.setEntity(entity); HttpResponse response = client.execute(post); HttpEntity httpEntity = response.getEntity(); result = EntityUtils.toString(httpEntity); Log.v("result", result); } catch(Exception e) { e.printStackTrace(); } 

这里是LIGHT WEIGHTED解决方案,对于我来说没有外部HTTPCore和这样的库。 我正面临着64K方法的问题,所以没有办法避免HTTPCore库

 import java.util.List; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.List; /** * This utility class provides an abstraction layer for sending multipart HTTP * POST requests to a web server. * * @author www.codejava.net */ public class MultipartUtility { private final String boundary; private static final String LINE_FEED = "\r\n"; private HttpURLConnection httpConn; private String charset; private OutputStream outputStream; private PrintWriter writer; /** * This constructor initializes a new HTTP POST request with content type * is set to multipart/form-data * * @param requestURL * @param charset * @throws IOException */ public MultipartUtility(String requestURL, String charset) throws IOException { this.charset = charset; // creates a unique boundary based on time stamp boundary = "===" + System.currentTimeMillis() + "==="; URL url = new URL(requestURL); httpConn = (HttpURLConnection) url.openConnection(); httpConn.setUseCaches(false); httpConn.setDoOutput(true); // indicates POST method httpConn.setDoInput(true); httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); httpConn.setRequestProperty("User-Agent", "CodeJava Agent"); httpConn.setRequestProperty("Test", "Bonjour"); outputStream = httpConn.getOutputStream(); writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true); } /** * Adds a form field to the request * * @param name field name * @param value field value */ public void addFormField(String name, String value) { writer.append("--" + boundary).append(LINE_FEED); writer.append("Content-Disposition: form-data; name=\"" + name + "\"") .append(LINE_FEED); writer.append("Content-Type: text/plain; charset=" + charset).append( LINE_FEED); writer.append(LINE_FEED); writer.append(value).append(LINE_FEED); writer.flush(); } /** * Adds a upload file section to the request * * @param fieldName name attribute in  * @param uploadFile a File to be uploaded * @throws IOException */ public void addFilePart(String fieldName, File uploadFile) throws IOException { String fileName = uploadFile.getName(); writer.append("--" + boundary).append(LINE_FEED); writer.append( "Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"") .append(LINE_FEED); writer.append( "Content-Type: " + URLConnection.guessContentTypeFromName(fileName)) .append(LINE_FEED); writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED); writer.append(LINE_FEED); writer.flush(); FileInputStream inputStream = new FileInputStream(uploadFile); byte[] buffer = new byte[4096]; int bytesRead = -1; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } outputStream.flush(); inputStream.close(); writer.append(LINE_FEED); writer.flush(); } /** * Adds a header field to the request. * * @param name - name of the header field * @param value - value of the header field */ public void addHeaderField(String name, String value) { writer.append(name + ": " + value).append(LINE_FEED); writer.flush(); } /** * Completes the request and receives response from the server. * * @return a list of Strings as response in case the server returned * status OK, otherwise an exception is thrown. * @throws IOException */ public List finish() throws IOException { List response = new ArrayList(); writer.append(LINE_FEED).flush(); writer.append("--" + boundary + "--").append(LINE_FEED); writer.close(); // checks server's status code first int status = httpConn.getResponseCode(); if (status == HttpURLConnection.HTTP_OK) { BufferedReader reader = new BufferedReader(new InputStreamReader( httpConn.getInputStream())); String line = null; while ((line = reader.readLine()) != null) { response.add(line); } reader.close(); httpConn.disconnect(); } else { throw new IOException("Server returned non-OK status: " + status); } return response; } } 

用法

 private void uploadMedia() { try { String charset = "UTF-8"; File uploadFile1 = new File("/sdcard/myvideo.mp4"); String requestURL = Data.BASE_URL+Data.URL_UPLOAD_REACTION_TEST; MultipartUtility multipart = new MultipartUtility(requestURL, charset); // multipart.addHeaderField("User-Agent", "CodeJava"); // multipart.addHeaderField("Test-Header", "Header-Value"); multipart.addFormField("friend_id", "Cool Pictures"); multipart.addFormField("userid", "Java,upload,Spring"); multipart.addFilePart("uploadedfile", uploadFile1); List response = multipart.finish(); Log.v("rht", "SERVER REPLIED:"); for (String line : response) { Log.v("rht", "Line : "+line); } } catch (Exception e) { e.printStackTrace(); } } 

接受上传的PHP代码

  

更轻松,轻便(32k),以及更多的性能:

Android异步Http客户端库: http : //loopj.com/android-async-http/

执行:

如何使用Volley在Android中发送“multipart / form-data”POST

尝试这个:

  public void SendMultipartFile() { Log.d(TAG, "UPLOAD: SendMultipartFile"); DefaultHttpClient httpclient = new DefaultHttpClient(); HttpPost httppost = new HttpPost(  ); File file = new File("/sdcard/spider.jpg"); Log.d(TAG, "UPLOAD: setting up multipart entity"); MultipartEntity mpEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); Log.d(TAG, "UPLOAD: file length = " + file.length()); Log.d(TAG, "UPLOAD: file exist = " + file.exists()); try { mpEntity.addPart("datafile", new FileBody(file, "application/octet")); mpEntity.addPart("id", new StringBody("1")); } catch (UnsupportedEncodingException e1) { Log.d(TAG, "UPLOAD: UnsupportedEncodingException"); e1.printStackTrace(); } httppost.setEntity(mpEntity); Log.d(TAG, "UPLOAD: executing request: " + httppost.getRequestLine()); Log.d(TAG, "UPLOAD: request: " + httppost.getEntity().getContentType().toString()); HttpResponse response; try { Log.d(TAG, "UPLOAD: about to execute"); response = httpclient.execute(httppost); Log.d(TAG, "UPLOAD: executed"); HttpEntity resEntity = response.getEntity(); Log.d(TAG, "UPLOAD: respose code: " + response.getStatusLine().toString()); if (resEntity != null) { Log.d(TAG, "UPLOAD: " + EntityUtils.toString(resEntity)); } if (resEntity != null) { resEntity.consumeContent(); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } 

我强烈建议Loopj。

我成功地使用它一次上传多个文件,包括不同的MIMEtypes。 只需做到这一点:

 File myVideo = new File("/path/to/myvideo.mp4"); File myPic = new File("/path/to/mypic.jpg"); RequestParams params = new RequestParams(); try { params.put("profile_picture", myPic); params.put("my_video", myVideo); } catch(FileNotFoundException e) {} 

对于大文件或多文件,您可能需要增加超时量,否则使用默认的超时时间,可能太短:

 client.setTimeout(500000) //make this the appropriate timeout in milliseconds 

请看这个链接的完整描述loopj和如何使用它,迄今为止我遇到的最简单的异步http库:

http://loopj.com/android-async-http/ http://loopj.com/android-async-http/doc/com/loopj/android/http/AsyncHttpClient.html

删除所有的httpclient,httpmime依赖项,并添加这个依赖项compile 'commons-httpclient:commons-httpclient:3.1' 。 这种依赖性已经建立在MultipartRequestEntity中,因此您可以轻松地将一个或多个文件上传到服务器

 public class FileUploadUrlConnection extends AsyncTask { private Context context; private String url; private List files; public FileUploadUrlConnection(Context context, String url, List files) { this.context = context; this.url = url; this.files = files; } @Override protected String doInBackground(String... params) { HttpClient client = new HttpClient(); PostMethod post = new PostMethod(url); HttpClientParams connectionParams = new HttpClientParams(); post.setRequestHeader(// Your header goes here ); try { Part[] parts = new Part[files.size()]; for (int i=0; i 

您也可以添加请求和响应超时

 client.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000); client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 10000); 

我可以推荐离子库它使用3依赖,你可以在这两个网站find所有三个jar文件:
https://github.com/koush/ion#jars(ion和androidasync)

https://code.google.com/p/google-gson/downloads/list(gson

 try { Ion.with(this, "http://www.urlthatyouwant.com/post/page") .setMultipartParameter("field1", "This is field number 1") .setMultipartParameter("field2", "Field 2 is shorter") .setMultipartFile("imagefile", new File(Environment.getExternalStorageDirectory()+"/testfile.jpg")) .asString() .setCallback(new FutureCallback() { @Override public void onCompleted(Exception e, String result) { System.out.println(result); }}); } catch(Exception e) { // Do something about exceptions System.out.println("exception: " + e); } 

这将运行异步和回调将在UI线程中执行一旦收到一个响应我强烈建议你去https://github.com/koush/ion进一步的信息

如果您使用AOSP库Volley这是一个简单的方法。

如下所示扩展类Request

 public class MultipartRequest extends Request { private static final String FILE_PART_NAME = "file"; private final Response.Listener mListener; private final Map mFilePart; private final Map mStringPart; MultipartEntityBuilder entity = MultipartEntityBuilder.create(); HttpEntity httpentity; public MultipartRequest(String url, Response.ErrorListener errorListener, Response.Listener listener, Map file, Map mStringPart) { super(Method.POST, url, errorListener); mListener = listener; mFilePart = file; this.mStringPart = mStringPart; entity.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); buildMultipartEntity(); } public void addStringBody(String param, String value) { mStringPart.put(param, value); } private void buildMultipartEntity() { for (Map.Entry entry : mFilePart.entrySet()) { // entity.addPart(entry.getKey(), new FileBody(entry.getValue(), ContentType.create("image/jpeg"), entry.getKey())); try { entity.addBinaryBody(entry.getKey(), Utils.toByteArray(new FileInputStream(entry.getValue())), ContentType.create("image/jpeg"), entry.getKey() + ".JPG"); } catch (FileNotFoundException e) { e.printStackTrace(); } } for (Map.Entry entry : mStringPart.entrySet()) { if (entry.getKey() != null && entry.getValue() != null) { 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 = entity.build(); httpentity.writeTo(bos); } catch (IOException e) { VolleyLog.e("IOException writing to ByteArrayOutputStream"); } return bos.toByteArray(); } @Override protected Response parseNetworkResponse(NetworkResponse response) { Log.d("Response", new String(response.data)); return Response.success(new String(response.data), getCacheEntry()); } @Override protected void deliverResponse(String response) { mListener.onResponse(response); } } 

你可以创建和添加一个请求 –

 Map params = new HashMap<>(); params.put("name", name.getText().toString()); params.put("email", email.getText().toString()); params.put("user_id", appPreferences.getInt( Utils.PROPERTY_USER_ID, -1) + ""); params.put("password", password.getText().toString()); params.put("imageName", pictureName); Map files = new HashMap<>(); files.put("photo", new File(Utils.LOCAL_RESOURCE_PATH + pictureName)); MultipartRequest multipartRequest = new MultipartRequest(Utils.BASE_URL + "editprofile/" + appPreferences.getInt(Utils.PROPERTY_USER_ID, -1), new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { // TODO Auto-generated method stub Log.d("Error: ", error.toString()); FugaDialog.showErrorDialog(ProfileActivity.this); } }, new Response.Listener() { @Override public void onResponse(String jsonResponse) { JSONObject response = null; try { Log.d("jsonResponse: ", jsonResponse); response = new JSONObject(jsonResponse); } catch (JSONException e) { e.printStackTrace(); } try { if (response != null && response.has("statusmessage") && response.getBoolean("statusmessage")) { updateLocalRecord(); } } catch (JSONException e) { e.printStackTrace(); } FugaDialog.dismiss(); } }, files, params); RequestQueue queue = Volley.newRequestQueue(this); queue.add(multipartRequest); 

为了后人,我没有看到okhttp提到。 相关文章。

基本上你使用一个MultipartBody.Builder来构建主体,然后在一个请求中发布这个主体。

在kotlin中的例子:

  val body = MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart( "file", file.getName(), RequestBody.create(MediaType.parse("image/png"), file) ) .addFormDataPart("timestamp", Date().time.toString()) .build() val request = Request.Builder() .url(url) .post(body) .build() httpClient.newCall(request).enqueue(object : okhttp3.Callback { override fun onFailure(call: Call?, e: IOException?) { ... } override fun onResponse(call: Call?, response: Response?) { ... } })