使用POST和HttpURLConnection发送文件

由于Android开发人员推荐使用HttpURLConnection类,所以我想知道是否有人可以提供一个关于如何通过POST将位图“文件”(实际上是内存stream)发送到Apache HTTP服务器的示例。 我不喜欢cookies或authentication或任何复杂的,但我只是想有一个可靠的逻辑实现。 我在这里看到的所有例子看起来更像是“让我们试试这个,也许它是有效的”。

现在,我有这个代码:

 URL url; HttpURLConnection urlConnection = null; try { url = new URL("http://example.com/server.cgi"); urlConnection = (HttpURLConnection) url.openConnection(); } catch (Exception e) { this.showDialog(getApplicationContext(), e.getMessage()); } finally { if (urlConnection != null) { urlConnection.disconnect(); } } 

showDialog应该只显示一个AlertDialog (如果URL无效?)。

现在,假设我生成了一个位图,如下所示: Bitmap image = this.getBitmap()在从View派生的控件中,我想通过POST发送它。 什么是适当的程序来实现这样的事情? 我需要使用哪些课程? 我可以在这个例子中使用HttpPost吗? 如果是这样,我将如何构build我的位图的InputStreamEntity ? 我会发现它需要首先将位图存储在设备上的文件中。


我还应该提到,我真的需要将原始位图的每一个不变的像素发送到服务器,所以我不能将其转换为JPEG。

我不知道为什么HttpURLConnection类不提供任何手段发送文件,而不必手动组成文件包装。 这是我最终做的,但如果有人知道更好的解决scheme,请让我知道。

input数据:

 Bitmap bitmap = myView.getBitmap(); 

静态的东西:

 String attachmentName = "bitmap"; String attachmentFileName = "bitmap.bmp"; String crlf = "\r\n"; String twoHyphens = "--"; String boundary = "*****"; 

设置请求:

 HttpURLConnection httpUrlConnection = null; URL url = new URL("http://example.com/server.cgi"); httpUrlConnection = (HttpURLConnection) url.openConnection(); httpUrlConnection.setUseCaches(false); httpUrlConnection.setDoOutput(true); httpUrlConnection.setRequestMethod("POST"); httpUrlConnection.setRequestProperty("Connection", "Keep-Alive"); httpUrlConnection.setRequestProperty("Cache-Control", "no-cache"); httpUrlConnection.setRequestProperty( "Content-Type", "multipart/form-data;boundary=" + this.boundary); 

开始内容包装:

 DataOutputStream request = new DataOutputStream( httpUrlConnection.getOutputStream()); request.writeBytes(this.twoHyphens + this.boundary + this.crlf); request.writeBytes("Content-Disposition: form-data; name=\"" + this.attachmentName + "\";filename=\"" + this.attachmentFileName + "\"" + this.crlf); request.writeBytes(this.crlf); 

Bitmap转换为ByteBuffer

 //I want to send only 8 bit black & white bitmaps byte[] pixels = new byte[bitmap.getWidth() * bitmap.getHeight()]; for (int i = 0; i < bitmap.getWidth(); ++i) { for (int j = 0; j < bitmap.getHeight(); ++j) { //we're interested only in the MSB of the first byte, //since the other 3 bytes are identical for B&W images pixels[i + j] = (byte) ((bitmap.getPixel(i, j) & 0x80) >> 7); } } request.write(pixels); 

结束内容包装:

 request.writeBytes(this.crlf); request.writeBytes(this.twoHyphens + this.boundary + this.twoHyphens + this.crlf); 

清除输出缓冲区:

 request.flush(); request.close(); 

得到回应:

 InputStream responseStream = new BufferedInputStream(httpUrlConnection.getInputStream()); BufferedReader responseStreamReader = new BufferedReader(new InputStreamReader(responseStream)); String line = ""; StringBuilder stringBuilder = new StringBuilder(); while ((line = responseStreamReader.readLine()) != null) { stringBuilder.append(line).append("\n"); } responseStreamReader.close(); String response = stringBuilder.toString(); 

closures响应stream:

 responseStream.close(); 

closures连接:

 httpUrlConnection.disconnect(); 

PS:当然,我不得不在private class AsyncUploadBitmaps extends AsyncTask<Bitmap, Void, String>的请求,以使Android平台开心,因为它不喜欢在主线程上有networking请求。

实际上我发现使用MultipartEntity使用HttpURLConnection发送文件更好

 private static String multipost(String urlString, MultipartEntity reqEntity) { try { URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(10000); conn.setConnectTimeout(15000); conn.setRequestMethod("POST"); conn.setUseCaches(false); conn.setDoInput(true); conn.setDoOutput(true); conn.setRequestProperty("Connection", "Keep-Alive"); conn.addRequestProperty("Content-length", reqEntity.getContentLength()+""); conn.addRequestProperty(reqEntity.getContentType().getName(), reqEntity.getContentType().getValue()); OutputStream os = conn.getOutputStream(); reqEntity.writeTo(conn.getOutputStream()); os.close(); conn.connect(); if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { return readStream(conn.getInputStream()); } } catch (Exception e) { Log.e(TAG, "multipart post error " + e + "(" + urlString + ")"); } return null; } private static String readStream(InputStream in) { BufferedReader reader = null; StringBuilder builder = new StringBuilder(); try { reader = new BufferedReader(new InputStreamReader(in)); String line = ""; while ((line = reader.readLine()) != null) { builder.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return builder.toString(); } 

假设您正在上传带有位图数据的图像:

  Bitmap bitmap = ...; String filename = "filename.png"; ByteArrayOutputStream bos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos); ContentBody contentPart = new ByteArrayBody(bos.toByteArray(), filename); MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); reqEntity.addPart("picture", contentPart); String response = multipost("http://server.com", reqEntity); 

和瞧! 您的发布数据将包含图片字段以及服务器上的文件名和path。

使用MultipartUtility以简单的方式用服务器上的某个参数上传文件。

MultipartUtility.java

 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); Log.e("URL", "URL : " + requestURL.toString()); 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 String finish() throws IOException { StringBuffer response = new StringBuffer(); 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.append(line); } reader.close(); httpConn.disconnect(); } else { throw new IOException("Server returned non-OK status: " + status); } return response.toString(); } } 

upload你的file和参数。

注意:把下面的代码放在非ui-thread中来得到响应。

 String charset = "UTF-8"; String requestURL = "YOUR_URL"; MultipartUtility multipart = new MultipartUtility(requestURL, charset); multipart.addFormField("param_name_1", "param_value"); multipart.addFormField("param_name_2", "param_value"); multipart.addFormField("param_name_3", "param_value"); multipart.addFilePart("file_param_1", new File(file_path)); String response = multipart.finish(); // response from server. 

Jaydipsinh Zala的解决scheme并不适合我,但我不知道为什么,因为它似乎太接近解决scheme。

所以我把这个与Mihai Todor的解决办法和解释结合起来,现在这个class对我很有用。 如果帮助某人:

MultipartUtility2V.java

 import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.nio.file.Files; public class MultipartUtilityV2 { private HttpURLConnection httpConn; private DataOutputStream request; private final String boundary = "*****"; private final String crlf = "\r\n"; private final String twoHyphens = "--"; /** * This constructor initializes a new HTTP POST request with content type * is set to multipart/form-data * * @param requestURL * @throws IOException */ public MultipartUtilityV2(String requestURL) throws IOException { // creates a unique boundary based on time stamp URL url = new URL(requestURL); httpConn = (HttpURLConnection) url.openConnection(); httpConn.setUseCaches(false); httpConn.setDoOutput(true); // indicates POST method httpConn.setDoInput(true); httpConn.setRequestMethod("POST"); httpConn.setRequestProperty("Connection", "Keep-Alive"); httpConn.setRequestProperty("Cache-Control", "no-cache"); httpConn.setRequestProperty( "Content-Type", "multipart/form-data;boundary=" + this.boundary); request = new DataOutputStream(httpConn.getOutputStream()); } /** * Adds a form field to the request * * @param name field name * @param value field value */ public void addFormField(String name, String value)throws IOException { request.writeBytes(this.twoHyphens + this.boundary + this.crlf); request.writeBytes("Content-Disposition: form-data; name=\"" + name + "\""+ this.crlf); request.writeBytes("Content-Type: text/plain; charset=UTF-8" + this.crlf); request.writeBytes(this.crlf); request.writeBytes(value+ this.crlf); request.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(); request.writeBytes(this.twoHyphens + this.boundary + this.crlf); request.writeBytes("Content-Disposition: form-data; name=\"" + fieldName + "\";filename=\"" + fileName + "\"" + this.crlf); request.writeBytes(this.crlf); byte[] bytes = Files.readAllBytes(uploadFile.toPath()); request.write(bytes); } /** * 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 String finish() throws IOException { String response =""; request.writeBytes(this.crlf); request.writeBytes(this.twoHyphens + this.boundary + this.twoHyphens + this.crlf); request.flush(); request.close(); // checks server's status code first int status = httpConn.getResponseCode(); if (status == HttpURLConnection.HTTP_OK) { InputStream responseStream = new BufferedInputStream(httpConn.getInputStream()); BufferedReader responseStreamReader = new BufferedReader(new InputStreamReader(responseStream)); String line = ""; StringBuilder stringBuilder = new StringBuilder(); while ((line = responseStreamReader.readLine()) != null) { stringBuilder.append(line).append("\n"); } responseStreamReader.close(); response = stringBuilder.toString(); httpConn.disconnect(); } else { throw new IOException("Server returned non-OK status: " + status); } return response; } } 

这个答案https://stackoverflow.com/a/33149413/6481542让我90%的方式上传大文件到Django服务器的开发,但我不得不使用setFixedLengthStreamingMode使其工作。; 这就需要在编写内容之前设置Content-Length,因此需要对上述答案进行相当重要的重写。 这是我的最终结果

 public class MultipartLargeUtility { private final String boundary; private static final String LINE_FEED = "\r\n"; private HttpURLConnection httpConn; private String charset; private OutputStream outputStream; private PrintWriter writer; private final int maxBufferSize = 4096; private long contentLength = 0; private URL url; private List<FormField> fields; private List<FilePart> files; private class FormField { public String name; public String value; public FormField(String name, String value) { this.name = name; this.value = value; } } private class FilePart { public String fieldName; public File uploadFile; public FilePart(String fieldName, File uploadFile) { this.fieldName = fieldName; this.uploadFile = uploadFile; } } /** * This constructor initializes a new HTTP POST request with content type * is set to multipart/form-data * * @param requestURL * @param charset * @throws IOException */ public MultipartLargeUtility(String requestURL, String charset, boolean requireCSRF) throws IOException { this.charset = charset; // creates a unique boundary based on time stamp boundary = "===" + System.currentTimeMillis() + "==="; url = new URL(requestURL); fields = new ArrayList<>(); files = new ArrayList<>(); if (requireCSRF) { getCSRF(); } } /** * Adds a form field to the request * * @param name field name * @param value field value */ public void addFormField(String name, String value) throws UnsupportedEncodingException { String fieldContent = "--" + boundary + LINE_FEED; fieldContent += "Content-Disposition: form-data; name=\"" + name + "\"" + LINE_FEED; fieldContent += "Content-Type: text/plain; charset=" + charset + LINE_FEED; fieldContent += LINE_FEED; fieldContent += value + LINE_FEED; contentLength += fieldContent.getBytes(charset).length; fields.add(new FormField(name, value)); } /** * 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(); String fieldContent = "--" + boundary + LINE_FEED; fieldContent += "Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"" + LINE_FEED; fieldContent += "Content-Type: " + URLConnection.guessContentTypeFromName(fileName) + LINE_FEED; fieldContent += "Content-Transfer-Encoding: binary" + LINE_FEED; fieldContent += LINE_FEED; // file content would go here fieldContent += LINE_FEED; contentLength += fieldContent.getBytes(charset).length; contentLength += uploadFile.length(); files.add(new FilePart(fieldName, uploadFile)); } /** * 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>(); String content = "--" + boundary + "--" + LINE_FEED; contentLength += content.getBytes(charset).length; if (!openConnection()) { return response; } writeContent(); // 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 boolean getCSRF() throws IOException { /// First, need to get CSRF token from server /// Use GET request to get the token CookieManager cookieManager = new CookieManager(); CookieHandler.setDefault(cookieManager); HttpURLConnection conn = null; conn = (HttpURLConnection) url.openConnection(); conn.setUseCaches(false); // Don't use a Cached Copy conn.setRequestMethod("GET"); conn.setRequestProperty("Connection", "Keep-Alive"); conn.getContent(); conn.disconnect(); /// parse the returned object for the CSRF token CookieStore cookieJar = cookieManager.getCookieStore(); List<HttpCookie> cookies = cookieJar.getCookies(); String csrf = null; for (HttpCookie cookie : cookies) { Log.d("cookie", "" + cookie); if (cookie.getName().equals("csrftoken")) { csrf = cookie.getValue(); break; } } if (csrf == null) { Log.d(TAG, "Unable to get CSRF"); return false; } Log.d(TAG, "Received cookie: " + csrf); addFormField("csrfmiddlewaretoken", csrf); return true; } private boolean openConnection() throws IOException { httpConn = (HttpURLConnection) url.openConnection(); httpConn.setUseCaches(false); httpConn.setDoOutput(true); // indicates POST method httpConn.setDoInput(true); //httpConn.setRequestProperty("Accept-Encoding", "identity"); httpConn.setFixedLengthStreamingMode(contentLength); httpConn.setRequestProperty("Connection", "Keep-Alive"); httpConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); outputStream = new BufferedOutputStream(httpConn.getOutputStream()); writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true); return true; } private void writeContent() throws IOException { for (FormField field : fields) { writer.append("--" + boundary).append(LINE_FEED); writer.append("Content-Disposition: form-data; name=\"" + field.name + "\"") .append(LINE_FEED); writer.append("Content-Type: text/plain; charset=" + charset).append( LINE_FEED); writer.append(LINE_FEED); writer.append(field.value).append(LINE_FEED); writer.flush(); } for (FilePart filePart : files) { String fileName = filePart.uploadFile.getName(); writer.append("--" + boundary).append(LINE_FEED); writer.append( "Content-Disposition: form-data; name=\"" + filePart.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(filePart.uploadFile); int bufferSize = Math.min(inputStream.available(), maxBufferSize); byte[] buffer = new byte[bufferSize]; int bytesRead = -1; while ((bytesRead = inputStream.read(buffer, 0, bufferSize)) != -1) { outputStream.write(buffer, 0, bytesRead); } outputStream.flush(); inputStream.close(); writer.append(LINE_FEED); writer.flush(); } writer.append("--" + boundary + "--").append(LINE_FEED); writer.close(); } } 

用法与上面的答案基本相同,但是我已经包含了Django在默认情况下使用表单的CSRF支持

 boolean useCSRF = true; MultipartLargeUtility multipart = new MultipartLargeUtility(url, "UTF-8",useCSRF); multipart.addFormField("param1","value"); multipart.addFilePart("filefield",new File("/path/to/file")); List<String> response = multipart.finish(); Log.w(TAG,"SERVER REPLIED:"); for(String line : response) { Log.w(TAG, "Upload Files Response:::" + line); } 

基于Mihai的解决scheme,如果任何人有像在我的服务器上发生的事情一样在服务器上保存图像的问题。 将Bitmap更改为bytebuffer部分:

 ByteArrayOutputStream bos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG,100,bos); byte[] pixels = bos.toByteArray(); 

我没有testing过这个,但是你可以尝试使用PipedInputStream和PipedOutputStream。 它可能看起来像这样:

 final Bitmap bmp = … // your bitmap // Set up Piped streams final PipedOutputStream pos = new PipedOutputStream(new ByteArrayOutputStream()); final PipedInputStream pis = new PipedInputStream(pos); // Send bitmap data to the PipedOutputStream in a separate thread new Thread() { public void run() { bmp.compress(Bitmap.CompressFormat.PNG, 100, pos); } }.start(); // Send POST request try { // Construct InputStreamEntity that feeds off of the PipedInputStream InputStreamEntity reqEntity = new InputStreamEntity(pis, -1); HttpClient httpclient = new DefaultHttpClient(); HttpPost httppost = new HttpPost(url); reqEntity.setContentType("binary/octet-stream"); reqEntity.setChunked(true); httppost.setEntity(reqEntity); HttpResponse response = httpclient.execute(httppost); } catch (Exception e) { e.printStackTrace() } 

这是我做了上传照片使用发布请求。

 public void uploadFile(int directoryID, String filePath) { Bitmap bitmapOrg = BitmapFactory.decodeFile(filePath); ByteArrayOutputStream bao = new ByteArrayOutputStream(); String upload_url = BASE_URL + UPLOAD_FILE; bitmapOrg.compress(Bitmap.CompressFormat.JPEG, 90, bao); byte[] data = bao.toByteArray(); HttpClient httpClient = new DefaultHttpClient(); HttpPost postRequest = new HttpPost(upload_url); MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); try { // Set Data and Content-type header for the image FileBody fb = new FileBody(new File(filePath), "image/jpeg"); StringBody contentString = new StringBody(directoryID + ""); entity.addPart("file", fb); entity.addPart("directory_id", contentString); postRequest.setEntity(entity); HttpResponse response = httpClient.execute(postRequest); // Read the response String jsonString = EntityUtils.toString(response.getEntity()); Log.e("response after uploading file ", jsonString); } catch (Exception e) { Log.e("Error in uploadFile", e.getMessage()); } } 

注:此代码需要库,请按照此处的说明获取库。

我发现使用okHttp更容易,因为我不能得到任何这些解决scheme的工作: https ://stackoverflow.com/a/37942387/447549