HTTP POST输出流与内存优化
来源:互联网 发布:求圆面积的编程 编辑:程序博客网 时间:2024/05/04 22:32
起因
有个功能,比如上传文件,想在HTTPPOST时候想打开一个OutputStream,然后将File Content通过FileInputStream边读边上传。以节省内存。
之前一直理解不到位,以前是这样实现的:
URL url = new URL("http://ddaitp.sinaapp.com/test.php"); HttpURLConnection conn =(HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setDoOutput(true); conn.setDoInput(true); conn.connect(); OutputStreamos = conn.getOutputStream();
后来发现这样没有解决问题,当文件比较大的时候依然会OutOfMemory。
原因
但是仔细读代码发现这样不能解决问题,还是将要上传的内容都读到内存,然后在flush() 时上传。
因为打印conn发现实际为libcore.net.http.ChunkedOutputStream。conn.getOutputStream()得到的OutputStream为RetryableOutputStream。
如果要实现目的,需要setFixedLengthStreamingMode()或者setChunkedStreamingMode(int)。
RetryableOutputStream中关键实现如下:
private final ByteArrayOutputStream content;public RetryableOutputStream() { this.limit = -1; this.content = new ByteArrayOutputStream(); }public synchronized void write(byte[] buffer, int offset, int count) throws IOException { checkNotClosed(); Arrays.checkOffsetAndCount(buffer.length, offset, count); if (limit != -1 && content.size() > limit - count) { throw new IOException("exceeded content-length limit of " + limit + " bytes"); } content.write(buffer, offset, count); }
所有的内容都写到了ByteArrayOutputStream中,存在于在内存。
关键代码如下:
libcore.net.http.HttpURLConnectionImpl
https://android.googlesource.com/platform/libcore/+/4f81a06/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java
public final OutputStream getOutputStream() throws IOException { connect(); OutputStreamresult = httpEngine.getRequestBody(); if (result == null) { throw new ProtocolException("method does not support a request body:" + method); } else if (httpEngine.hasResponse()) { throw new ProtocolException("cannot write request body afterresponse has been read"); } return result; }
libcore.net.http.HttpEngine
https://android.googlesource.com/platform/libcore/+/4f81a06/luni/src/main/java/libcore/net/http/HttpEngine.java
protected void initRequestBodyOut() throws IOException { int chunkLength = policy.getChunkLength(); if (chunkLength > 0 || requestHeaders.isChunked()) { sendChunked = true; if (chunkLength == -1) { chunkLength = DEFAULT_CHUNK_LENGTH; } } if (socketOut == null) { throw new IllegalStateException("No socket to write to; was a POSTcached?"); } if (httpMinorVersion == 0) { sendChunked = false; } intfixedContentLength = policy.getFixedContentLength(); if (requestBodyOut != null) { // request body was already initialized bythe predecessor HTTP engine } else if (fixedContentLength != -1) { writeRequestHeaders(fixedContentLength); requestBodyOut = new FixedLengthOutputStream(requestOut, fixedContentLength); } else if (sendChunked) { writeRequestHeaders(-1); requestBodyOut = new ChunkedOutputStream(requestOut, chunkLength); } else if (requestHeaders.getContentLength() != -1) { writeRequestHeaders(requestHeaders.getContentLength()); requestBodyOut = new RetryableOutputStream(requestHeaders.getContentLength()); } else { requestBodyOut = new RetryableOutputStream(); } }}
修改
结论,修改办法也简单。有两种办法
Chunked Streaming
添加conn.setChunkedStreamingMode(0);
修改后如下:
HttpURLConnection conn; try { conn = (HttpURLConnection) new URL( "http://10.2.167.14/").openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("User-Agent", "ddai-agent"); conn.setChunkedStreamingMode(0); conn.setConnectTimeout(0); conn.setReadTimeout(0); conn.setDoOutput(true); conn.setDoInput(true); conn.connect(); BufferedOutputStream os = new BufferedOutputStream( conn.getOutputStream()); upload(os, conn); os.flush(); os.close(); conn.disconnect(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
Fixed Length
设置POST内容的总长度:
conn.setFixedLengthStreamingMode();
由于我写出只是一个File,所以长度不包括HTTP标准的其他内容。
HttpURLConnection conn; try { conn = (HttpURLConnection) new URL( "http://ddaitp.sinaapp.com/test.php").openConnection(); conn.setRequestMethod("POST"); conn.setDoOutput(true); conn.setDoInput(true); conn.setFixedLengthStreamingMode((int) uploadFile.length()); conn.connect(); OutputStream os =conn.getOutputStream(); upload(os, conn); conn.disconnect(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
测试
上传同一个文件,SDCARD/aa.rar 20MB左右
未上传时候:
用以前的错误方式上传,上传过程中内存占用一直上升,然后OutOfMemory。崩溃时内存占用:
然后是Chunked方式:
上传过程中内存占用稳定的保持很低。
Fiexd Length 方式:
和Chunked方式差不多一直很低。
- HTTP POST输出流与内存优化
- http get 与 post
- http post 与put
- HTTP中 POST 与 GET
- Http协议GET与POST
- HTTP GET与POST区别
- HTTP中Get与Post
- http中get与post
- Http中的Get与Post
- http协议--get与post
- HTTP中的GET与POST
- HTTP中Post与Get
- http的Get与Post
- HTTP Get 与 HTTP Post 详细介绍
- Http POST 与 Http GET 的区别
- Android通过http协议POST传输方式(输出流提交到服务端)
- AngularJS中的$http.post与jQuery.post的区别
- HTTP get、post请求与post文件发生
- jQuery中find()的使用
- Linux下Shell编程——sed命令基本用法
- 瓷砖覆盖地板
- 据说三分之一左右的小米盒子安装了第三方兔子桌面?
- 广州传智播客助你突围最难就业季
- HTTP POST输出流与内存优化
- udev详解
- jQuery中eq()的使用
- C语言中截断数字(比如int转换为short)处理规则
- CGAffineTransformMakeRotation 实现旋转
- 软件测试面试故事--聪明反被聪明误
- 详细解析Linux /etc/passwd文件
- c++中WaitForSingleObject函数解析
- Linux下动态库的生成和使用