所以我正在尝试发送带有大图像文件的multipart / form-data POST请求.我不能预先将文件转换为字节数组,我的应用程序会因OutOfMemory异常而崩溃,所以我必须将文件的内容直接写入连接的输出流.此外,我的服务器不支持分块模式,因此我必须在发送数据之前计算内容长度并使用连接的setFixedLengthStreamingMode.
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,bufferSize); while (bytesRead > 0) { outputStream.write(buffer,bufferSize); bytesAvailable = fileInputStream.available(); bufferSize = Math.min(bytesAvailable,maxBufferSize); bytesRead = fileInputStream.read(buffer,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 } }
不幸的是,我的应用程序在outputStream.close()时崩溃了IOException,我不明白为什么:
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)
解决方法
outputStream.close()之后的conn.disconnect()