Próbuję zrobić coś, co moim zdaniem byłoby stosunkowo proste: przesłać obraz na serwer za pomocą Android SDK. Znalazłem dużo przykładowego kodu:
http://linklens.blogspot.com/2009/06/android-multipart-upload.html
Ale to nie działa dla mnie. Zamieszanie, na które ciągle napotykam, jest tym, co jest naprawdę potrzebne, aby wykonać żądanie wieloczęściowe. Jaki jest najprostszy sposób przesłania wielu części (z obrazem) na Androida?
Każda pomoc lub rada byłaby bardzo ceniona!
Odpowiedzi:
Aktualizacja 29 kwietnia 2014:
Moja odpowiedź jest już trochę stara i myślę, że wolisz użyć jakiejś biblioteki wysokiego poziomu, takiej jak Retrofit .
Na podstawie tego bloga wymyśliłem następujące rozwiązanie: http://blog.tacticalnuclearstrike.com/2010/01/using-multipartentity-in-android-applications/
Aby rozpocząć
MultipartEntity
działanie, musisz pobrać dodatkowe biblioteki !1) Pobierz plik httpcomponents-client-4.1.zip z http://james.apache.org/download.cgi#Apache_Mime4J i dodaj apache-mime4j-0.6.1.jar do swojego projektu.
2) Pobierz httpcomponents-client-4.1-bin.zip z http://hc.apache.org/downloads.cgi i dodaj do swojego projektu httpclient-4.1.jar, httpcore-4.1.jar i httpmime-4.1.jar.
3) Użyj poniższego przykładowego kodu.
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; } }
źródło
MultipartEntity
jest teraz przestarzała. Ten post może być wskazówką dla tych, którzy chcąMultipartEntityBuilder
zamiast tego użyć : stackoverflow.com/a/19188010/1276636Jak
MultiPartEntity
jest przestarzałe . Oto nowy sposób na zrobienie tego! Potrzebujesz tylkohttpcore.jar(latest)
ihttpmime.jar(latest)
pobierzesz je ze strony 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(); }
źródło
MultipartEntityBuilder
?entityBuilder.addBinaryBody(key, file);
upewnij się, że klucz jest unikalny.Oto LIGHT WEIGHTED rozwiązanie, które działało dla mnie bez zewnętrznego HTTPCore i takich bibliotek. Miałem do czynienia z problemem metod 64K, więc nie mam opcji, aby uniknąć bibliotek 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; } }
STOSOWANIE
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(); } }
Kod PHP do akceptacji przesyłania
<?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"]); ?>
źródło
Łatwiejsze, lżejsze (32 kB) i dużo więcej wydajności:
Biblioteka klienta asynchronicznego klienta HTTP dla systemu Android: http://loopj.com/android-async-http/
Realizacja:
Jak wysłać POST „multipart / form-data” w Androidzie za pomocą Volley
źródło
Spróbuj tego:
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(); } }
źródło
Bardzo polecam Loopj.
Z powodzeniem wykorzystałem go do przesyłania wielu plików jednocześnie, w tym różnych typów MIME. Po prostu zrób to:
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) {}
W przypadku dużych lub wielu plików może być konieczne zwiększenie limitu czasu, w przeciwnym razie używany jest domyślny limit czasu, który może być zbyt krótki:
client.setTimeout(500000) //make this the appropriate timeout in milliseconds
Proszę zobaczyć te linki, aby uzyskać pełny opis loopj i jak go używać, zdecydowanie najłatwiejszą bibliotekę asynchroniczną http, z jaką się spotkałem:
http://loopj.com/android-async-http/ http://loopj.com/android-async-http/doc/com/loopj/android/http/AsyncHttpClient.html
źródło
Usuń wszystkie zależności httpclient i httpmime i dodaj tę zależność
compile 'commons-httpclient:commons-httpclient:3.1'
. Ta zależność ma wbudowaną MultipartRequestEntity, dzięki czemu można łatwo przesłać jeden lub więcej plików na serwerpublic 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; }
Możesz również dodać żądanie i limit czasu odpowiedzi
client.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000); client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 10000);
źródło
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()); } }
Zadzwoń poniżej
Multipart multipart = new Multipart(url__, map); multipart .connectForMultipart(); multipart .addFormPart(entry.getKey(), entry.getValue()); multipart .addFilePart(KeyName, "FileName", imagedata); multipart .finishMultipart();
źródło
Mogę polecić bibliotekę Ion, która używa 3 zależności i możesz znaleźć wszystkie trzy pliki jar na tych dwóch stronach:
https://code.google.com/p/google-gson/downloads/list (gson)https://github.com/koush/ion#jars (ion i 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); }
to będzie działać asynchronicznie, a wywołanie zwrotne zostanie wykonane w wątku interfejsu użytkownika po otrzymaniu odpowiedzi. Zdecydowanie polecam, aby przejść do https://github.com/koush/ion w celu uzyskania dalszych informacji
źródło
Oto proste podejście, jeśli używasz biblioteki AOSP
Volley
.Rozszerz klasę
Request<T>
w następujący sposób: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); } }
Możesz utworzyć i dodać żądanie, takie jak-
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);
źródło
Dla potomnych nie widziałem wspomnianego okhttp. Powiązany post.
Zasadniczo budujesz ciało za pomocą MultipartBody.Builder, a następnie publikujesz to w żądaniu.
Przykład w 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?) { ... } })
źródło
Możesz użyć GentleRequest, która jest lekką biblioteką do tworzenia żądań http (ZRZECZENIE SIĘ: jestem autorem):
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 { }
Zapraszam do sprawdzenia: https://github.com/Iprogrammerr/Gentle-Request
źródło