Я пытаюсь сделать то, что, как мне казалось, будет относительно простым: загрузить изображение на сервер с помощью Android SDK. Я нашел много примеров кода:
http://linklens.blogspot.com/2009/06/android-multipart-upload.html
Но ни то, ни другое не работает для меня. Путаница, с которой я постоянно сталкиваюсь, - это то, что действительно необходимо для выполнения многостраничного запроса. Каков самый простой способ сделать загрузку нескольких частей (с изображением) для Android?
Любая помощь или совет будут приняты с благодарностью!
Ответы:
Обновление 29 апреля 2014 г .:
Мой ответ уже устарел, и я думаю, вы скорее захотите использовать какую-то библиотеку высокого уровня, такую как Retrofit .
На основе этого блога я придумал следующее решение: http://blog.tacticalnuclearstrike.com/2010/01/using-multipartentity-in-android-applications/
Вам придется загрузить дополнительные библиотеки, чтобы начать
MultipartEntity
работу!1) Загрузите httpcomponents-client-4.1.zip с http://james.apache.org/download.cgi#Apache_Mime4J и добавьте apache-mime4j-0.6.1.jar в свой проект.
2) Загрузите httpcomponents-client-4.1-bin.zip с http://hc.apache.org/downloads.cgi и добавьте в свой проект 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<Object> { @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
сейчас он устарел. Этот пост может помочь тем, кто хочет использовать егоMultipartEntityBuilder
вместо этого: stackoverflow.com/a/19188010/1276636Как
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(); }
источник
MultipartEntityBuilder
?entityBuilder.addBinaryBody(key, file);
убедитесь, что ключ уникален.Вот 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 <input type="file" name="..." /> * @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<String> finish() throws IOException { List<String> response = new ArrayList<String>(); 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<String> response = multipart.finish(); Log.v("rht", "SERVER REPLIED:"); for (String line : response) { Log.v("rht", "Line : "+line); } } catch (Exception e) { e.printStackTrace(); } }
PHP-код для принятия загрузки
<?php $friend_id = $_REQUEST['friend_id']; $userid = $_REQUEST['userid']; echo 'friend_id : '.$friend_id. ' userid '.$userid; move_uploaded_file($_FILES['uploadedfile']['tmp_name'], "./uploads/".$_FILES["uploadedfile"]["name"]); ?>
источник
Более простой, легкий (32k) и гораздо более производительный:
Клиентская библиотека асинхронного HTTP для Android: http://loopj.com/android-async-http/
Реализация:
Как отправить POST-запрос multipart / form-data на Android с помощью Volley
источник
Попробуй это:
public void SendMultipartFile() { Log.d(TAG, "UPLOAD: SendMultipartFile"); DefaultHttpClient httpclient = new DefaultHttpClient(); HttpPost httppost = new HttpPost( <url> ); 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.
Я успешно использовал его для одновременной загрузки нескольких файлов, в том числе разных типов mime. Просто сделайте это:
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<String, String, String> { private Context context; private String url; private List<File> files; public FileUploadUrlConnection(Context context, String url, List<File> 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<files.size(); i++) { Part part = new FilePart(files.get(i).getName(), files.get(i)); parts[i] = part; } MultipartRequestEntity entity = new MultipartRequestEntity(parts, connectionParams); post.setRequestEntity(entity); int statusCode = client.executeMethod(post); String response = post.getResponseBodyAsString(); Log.v("Multipart "," "+response); if(statusCode == 200) { return response; } } catch (IOException e) { e.printStackTrace(); } return null; }
Вы также можете добавить таймаут запроса и ответа
client.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000); client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 10000);
источник
public class Multipart{ private final Map<String, String> headrs; private String url; private HttpURLConnection con; private OutputStream os; private String delimiter = "--"; private String boundary = "TRR" + Long.toString(System.currentTimeMillis()) + "TRR"; public Multipart (String url, Map<String, String> headers) { this.url = url; this.headrs = headers; } public void connectForMultipart() throws Exception { con = (HttpURLConnection) (new URL(url)).openConnection(); con.setRequestMethod("POST"); con.setDoInput(true); con.setDoOutput(true); con.setRequestProperty("Connection", "Keep-Alive"); for (Map.Entry<String, String> entry : headrs.entrySet()) { con.setRequestProperty(entry.getKey(), entry.getValue()); } con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); con.connect(); os = con.getOutputStream(); } public void addFormPart(String paramName, String value) throws Exception { writeParamData(paramName, value); } public void addFilePart(String paramName, String fileName, byte[] data) throws Exception { os.write((delimiter + boundary + "\r\n").getBytes()); os.write(("Content-Disposition: form-data; name=\"" + paramName + "\"; filename=\"" + fileName + "\"\r\n").getBytes()); os.write(("Content-Type: application/octet-stream\r\n").getBytes()); os.write(("Content-Transfer-Encoding: binary\r\n").getBytes()); os.write("\r\n".getBytes()); os.write(data); os.write("\r\n".getBytes()); } public void finishMultipart() throws Exception { os.write((delimiter + boundary + delimiter + "\r\n").getBytes()); } public String getResponse() throws Exception { InputStream is = con.getInputStream(); byte[] b1 = new byte[1024]; StringBuffer buffer = new StringBuffer(); while (is.read(b1) != -1) buffer.append(new String(b1)); con.disconnect(); return buffer.toString(); } private void writeParamData(String paramName, String value) throws Exception { os.write((delimiter + boundary + "\r\n").getBytes()); os.write("Content-Type: text/plain\r\n".getBytes());//;charset=utf-8 os.write(("Content-Disposition: form-data; name=\"" + paramName + "\"\r\n").getBytes()); ; os.write(("\r\n" + value + "\r\n").getBytes()); } }
Тогда звоните ниже
Multipart multipart = new Multipart(url__, map); multipart .connectForMultipart(); multipart .addFormPart(entry.getKey(), entry.getValue()); multipart .addFilePart(KeyName, "FileName", imagedata); multipart .finishMultipart();
источник
Я могу порекомендовать библиотеку Ion, в которой используются 3 зависимости, и вы можете найти все три файла jar на этих двух сайтах:
https://code.google.com/p/google-gson/downloads/list (gson)https://github.com/koush/ion#jars (ion и androidasync)
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<String>() { @Override public void onCompleted(Exception e, String result) { System.out.println(result); }}); } catch(Exception e) { // Do something about exceptions System.out.println("exception: " + e); }
это будет работать асинхронно, и обратный вызов будет выполнен в потоке пользовательского интерфейса после получения ответа. Я настоятельно рекомендую вам перейти на https://github.com/koush/ion для получения дополнительной информации.
источник
Вот простой подход, если вы используете библиотеку AOSP
Volley
.Расширьте класс
Request<T>
следующим образом:public class MultipartRequest extends Request<String> { private static final String FILE_PART_NAME = "file"; private final Response.Listener<String> mListener; private final Map<String, File> mFilePart; private final Map<String, String> mStringPart; MultipartEntityBuilder entity = MultipartEntityBuilder.create(); HttpEntity httpentity; public MultipartRequest(String url, Response.ErrorListener errorListener, Response.Listener<String> listener, Map<String, File> file, Map<String, String> 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<String, File> 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<String, String> 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<String> 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<String, String> 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<String, File> 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<String>() { @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, а затем отправляете его в запрос.
Пример на котлине:
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?) { ... } })
источник
Вы можете использовать GentleRequest, легкую библиотеку для выполнения HTTP-запросов (ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: я являюсь автором):
Connections connections = new HttpConnections(); Binary binary = new PacketsBinary(new BufferedInputStream(new FileInputStream(file)), file.length()); //Content-Type is set to multipart/form-data; boundary= //{generated by multipart object} MultipartForm multipart = new HttpMultipartForm( new HttpFormPart("user", "aplication/json", new JSONObject().toString().getBytes()), new HttpFormPart("java", "java.png", "image/png", binary.content())); Response response = connections.response(new PostRequest(url, multipart)); if (response.hasSuccessCode()) { byte[] raw = response.body().value(); String string = response.body().stringValue(); JSONOBject json = response.body().jsonValue(); } else { }
Не стесняйтесь проверить это: https://github.com/Iprogrammerr/Gentle-Request
источник