HTTPURLConnection - POST multipart / form-data with large file with FixedLengthStreamingMode

So, I am trying to send a POST request with multiple data / data forms with a large image file. I cannot convert the file to an array of bytes beforehand, my application will fail with the exception of OutOfMemory, so I have to write the contents of the file directly to the connection output stream. In addition, my server does not support chunked mode, so I need to calculate the length of the content before sending data and use the setFixedLengthStreamingMode connection.

public void createImagePostWithToken(String accessToken, String text, String type, String imagePath) { URL imageUrl = null; String lineEnd = "\r\n"; String twoHyphens = "--"; // generating byte[] boundary here HttpURLConnection conn = null; DataOutputStream outputStream = null; DataInputStream inputStream = null; int bytesRead, bytesAvailable, bufferSize; byte[] buffer; int maxBufferSize = 1*1024*1024; try { long contentLength; int serverResponseCode; String serverResponseMessage; File file = new File(imagePath); FileInputStream fileInputStream = new FileInputStream(file); imageUrl = buildUri("posts").toURL(); conn = (HttpURLConnection)imageUrl.openConnection(); conn.setConnectTimeout(30000); conn.setReadTimeout(30000); conn.setDoOutput(true); conn.setDoInput(true); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); String stringForLength = new String(); stringForLength += "Content-Type: multipart/form-data;boundary=" + boundary; stringForLength += twoHyphens + boundary + lineEnd + "Content-Disposition: form-data; name=\"access_token\"" + lineEnd; stringForLength += "Content-Type: text/plain;charset=UTF-8" + lineEnd + "Content-Length: " + accessToken.length() + lineEnd + lineEnd; stringForLength += accessToken + lineEnd + twoHyphens + boundary + lineEnd; stringForLength += "Content-Disposition: form-data; name=\"text\"" + lineEnd; stringForLength += "Content-Type: text/plain;charset=UTF-8" + lineEnd + "Content-Length: " + text.length() + lineEnd + lineEnd; stringForLength += text + lineEnd + twoHyphens + boundary + lineEnd; stringForLength += "Content-Disposition: form-data; name=\"type\"" + lineEnd; stringForLength += "Content-Type: text/plain;charset=UTF-8" + lineEnd + "Content-Length: " + type.length() + lineEnd + lineEnd; stringForLength += type + lineEnd + twoHyphens + boundary + lineEnd; stringForLength += twoHyphens + boundary + lineEnd + "Content-Disposition: form-data; name=\"image\"" + lineEnd; stringForLength += "Content-Type: application/octet-stream" + lineEnd + "Content-Length: " + file.length() + lineEnd + lineEnd; stringForLength += lineEnd + twoHyphens + boundary + twoHyphens + lineEnd; int totalLength = stringForLength.length() + (int)file.length(); conn.setFixedLengthStreamingMode(totalLength); outputStream = new DataOutputStream( conn.getOutputStream() ); outputStream.writeBytes(twoHyphens + boundary + lineEnd); // access token outputStream.writeBytes("Content-Disposition: form-data; name=\"access_token\"" + lineEnd); outputStream.writeBytes("Content-Type: text/plain;charset=UTF-8" + lineEnd); outputStream.writeBytes("Content-Length: " + accessToken.length() + lineEnd); outputStream.writeBytes(lineEnd); outputStream.writeBytes(accessToken + lineEnd); outputStream.writeBytes(twoHyphens + boundary + lineEnd); // text outputStream.writeBytes("Content-Disposition: form-data; name=\"text\"" + lineEnd); outputStream.writeBytes("Content-Type: text/plain;charset=UTF-8" + lineEnd); outputStream.writeBytes("Content-Length: " + text.length() + lineEnd); outputStream.writeBytes(lineEnd); outputStream.writeBytes(text + lineEnd); outputStream.writeBytes(twoHyphens + boundary + lineEnd); // type outputStream.writeBytes("Content-Disposition: form-data; name=\"type\"" + lineEnd); outputStream.writeBytes("Content-Type: text/plain;charset=UTF-8" + lineEnd); outputStream.writeBytes("Content-Length: " + type.length() + lineEnd); outputStream.writeBytes(lineEnd); outputStream.writeBytes(type + lineEnd); outputStream.writeBytes(twoHyphens + boundary + lineEnd); // image outputStream.writeBytes(twoHyphens + boundary + lineEnd); outputStream.writeBytes("Content-Disposition: form-data; name=\"image\"" + lineEnd); //outputStream.writeBytes(lineEnd); outputStream.writeBytes("Content-Type: application/octet-stream" + lineEnd); outputStream.writeBytes("Content-Length: " + file.length() + lineEnd); outputStream.writeBytes(lineEnd); bytesAvailable = fileInputStream.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); buffer = new byte[bufferSize]; // Read file bytesRead = fileInputStream.read(buffer, 0, bufferSize); while (bytesRead > 0) { outputStream.write(buffer, 0, bufferSize); bytesAvailable = fileInputStream.available(); bufferSize = Math.min(bytesAvailable, maxBufferSize); bytesRead = fileInputStream.read(buffer, 0, bufferSize); } outputStream.writeBytes(lineEnd); outputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); Log.d("posttemplate", "connection outputstream size is " + outputStream.size()); // finished with POST request body // Responses from the server (code and message) serverResponseCode = conn.getResponseCode(); serverResponseMessage = conn.getResponseMessage(); Log.d("posttemplate", "server response code "+ serverResponseCode); Log.d("posttemplate", "server response message "+ serverResponseMessage); fileInputStream.close(); conn.disconnect(); outputStream.flush(); outputStream.close(); } catch (MalformedURLException e) { Log.d("posttemplate", "malformed url", e); //TODO: catch exception; } catch (IOException e) { Log.d("posttemplate", "ioexception", e); //TODO: catch exception } } 

Unfortunately, my application crashes from IOException to outputStream.close (), and I have no idea why:

 03-16 13:56:51.035: D/posttemplate(6479): java.io.IOException: unexpected end of stream 03-16 13:56:51.035: D/posttemplate(6479): at org.apache.harmony.luni.internal.net.www.protocol.http.FixedLengthOutputStream.close(FixedLengthOutputStream.java:57) 03-16 13:56:51.035: D/posttemplate(6479): at java.io.FilterOutputStream.close(FilterOutputStream.java:66) 03-16 13:56:51.035: D/posttemplate(6479): at com.futubra.api.impl.PostTemplate.createImagePostWithToken(PostTemplate.java:282) 03-16 13:56:51.035: D/posttemplate(6479): at com.futubra.FutubraNewPostActivity.createPost(FutubraNewPostActivity.java:128) 03-16 13:56:51.035: D/posttemplate(6479): at com.futubra.FutubraNewPostActivity_.access$2(FutubraNewPostActivity_.java:1) 03-16 13:56:51.035: D/posttemplate(6479): at com.futubra.FutubraNewPostActivity_$5.run(FutubraNewPostActivity_.java:141) 03-16 13:56:51.035: D/posttemplate(6479): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088) 03-16 13:56:51.035: D/posttemplate(6479): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581) 03-16 13:56:51.035: D/posttemplate(6479): at java.lang.Thread.run(Thread.java:1019) 
+6
source share
4 answers

conn.disconnect() after outputStream.close()

+5
source

The HTTP header is not part of the body, so do not consider them for body length.

  stringForLength += "Content-Type: multipart/form-data;boundary=" + boundary; 

delete the line above so that the content length is correct.

+2
source

For your information, your code should work well with the HTTP HTTP connection, but HTTPS will cause a "Out of Memory" error, because in Android 2.3.4 (checked on my tablet) there is an error in HttpsURLConnectionImpl.java, and this error has been fixed in Android 4.1 (I checked the source code).

+1
source
  int totalLength = stringForLength.length() + (int)file.length(); 

Your code above is incorrect.

You should use the byte length of the string as shown below:

  int totalLength = stringForLength.getBytes().length + (int)file.length(); 
0
source

Source: https://habr.com/ru/post/910893/


All Articles