`
ferreousbox
  • 浏览: 287500 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

HttpURLConnection的流式输出的缺陷和解决方法

    博客分类:
  • java
阅读更多
    最近在用applet写文件上传控件的时候发现使用URLConnection来对服务器进行流式输出时的一些问题.我们通常要对服务器上的某个地址进行写流操作,那么我们一般的做法就是:

URLConnection con = new URL("/test.do").openConnection();
con.setDoOutput(true); // 允许输出流,默认是false


    这样我们就获取一个到/test.do地址的HTTP连接了,我们打印con的class后发现其实是:sun.net.www.protocol.http.HttpURLConnection这个类,我们在写大数据流到服务器上时就会发现总是会出现OutOfMemoryError的错误,当然不同的机器上出现错误所对应的文件输出流的大小是不一样的.这个主要就是取决于本机的JVM的内存空间的大小了.出现OutOfMemoryError错误的主要原因就是:sun公司实现的HttpURLConnection的输出流是首先在本地内存进行缓存,然后再一次性输出的(在close操作时).我们可以追踪到sun自己的HttpURLConnection使用的OutputStream是sun.net.www.http.PosterOutputStream这个类,我们查看这个类的源码就会发现它是继承自ByteArrayOutputStream的,而且基本上这个类没有做任何事情,大家可以参看其源码.而我们的ByteArrayOutputStream则是每次将要输出的内容复制到一个byte数组中,从而导致的结果是将整个输出流全部存储在内存中,这样当我们输出流一大的时候就会出现内存不够用的情况.请看ByteArrayOutputStream的部分源码:

public synchronized void write(int i) 
{ 
    int j = count + 1; 
    if(j > buf.length) { 
        byte abyte0[] = new byte[Math.max(buf.length << 1, j)]; 
        System.arraycopy(buf, 0, abyte0, 0, count); 
        buf = abyte0; 
    } 
    buf[count] = (byte)i; 
    count = j; 
} 

public synchronized void write(byte abyte0[], int i, int j) 
{ 
    if(i < 0 || i > abyte0.length || j < 0 || i + j > abyte0.length || i + j < 0) throw new IndexOutOfBoundsException(); 
    if(j == 0)  return; 
    int k = count + j; 
    if(k > buf.length) { 
        byte abyte1[] = new byte[Math.max(buf.length << 1, k)]; 
        System.arraycopy(buf, 0, abyte1, 0, count); 
        buf = abyte1; 
    } 
    System.arraycopy(abyte0, i, buf, count, j); 
    count = k; 
}

    我们可以看到它是使用System.arraycopy的功能来将所有的输出流存放在一个数组中的.因此,在使用HttpURLConnection进行流式输出的时候如果输出流比较大,那么就该考虑使用其他方式了(当然,修改JVM的堆栈空间是一种方法,但是不可取).

    这是我们直接使用java.net.HttpURLConnection类的相关方法来进行输出文件流,我们查看sun提供的HttpURLConnection的源码,会发现其默认是采用上面提高的PosterOutputStream类来进行缓冲输出的,即首先将所有的文件流在本地内存中进行缓存,等到输出结束执行close的时候一次性输出到服务器端.同时我们看到sun的HttpURLConnection中的getOutputStream()中有如下代码:

if(streaming()) {
    if(fixedContentLength != -1) 
        strOutputStream = new StreamingOutputStream(ps, fixedContentLength); 
    else if(chunkLength != -1) 
        strOutputStream = new StreamingOutputStream(new ChunkedOutputStream(ps, chunkLength), -1); 
    return strOutputStream; 
}


    其中strmeanming()方法是用来判断是否是流式的输出,其代码为:return fixedContentLength != -1 || chunkLength != -1;它的判断方法就是如果设置了输出流的固定长度或是设置了块的长度,那么将采用流式输出.因此,我们可以在输出的时候可以设置其长度来达到流式输出这样的效果.另外,StreamingOutputString类是sun提供的HttpURLConnection的内部类,继承自FilterOutputStream,而非ByteArrayOutputStream,所以不会在本地内存中进行缓存.

    而jdk中的HttpURLConnection并没有提供设置流的固定长度或块输出的长度方法,所以我们需要显示的将new URL("url").openConnection()返回的URLConnection转换成sun的HttpURLConnection,从而我们就可以很方便的使用setFixedLengthStreamingMode方法来设置流的固定长度,那么也就会采用流式的输出了.那么也就不会出现OutOfMemoryError的错误了.另外,ChunkedOutputStream也是不会在本地进行缓存的,它是使用固定大小的数组来缓存输出流,等缓存满的时候就自动的调用基础流进行输出,这个主要是用在无法确定输出流的具体长度但是又不想在本地进行缓存时用到.同理,我们通过设置setChunkedStreamingMode就可以达到这样的效果,三种方式的代码如下:

第1种方式:使用直接输出流的方式(已知输出流的长度):
import sun.net.www.protocol.http.HttpURLConnection;
......
HttpURLConnection con = (HttpURLConnection)new URL("url").openConnection();
con.setFixedLengthStreamingMode(输出流的固定长度);
......



第2种方式:使用直接输出流的方式(未知输出流的长度):
import sun.net.www.protocol.http.HttpURLConnection;
......
HttpURLConnection con = (HttpURLConnection)new URL("url").openConnection();
con.setChunkedStreamingMode(块的大小);
......



第3种方式:本地缓存后一次性输出:
import java.net.HttpURLConnection;
......
HttpURLConnection con = (HttpURLConnection)new URL("url").openConnection();
......


    通过设置直接输出后,我传送文件的大小为200M的时候也不会出现OutOfMemoryError的错误,而之前使用第3种方式进行文件流输出的时候不到40M就出现了OutOfMemoryError的错误了.
分享到:
评论
11 楼 单眼皮大娘 2015-07-03  
写的非常好  现在的JDK已经可以设置固定大小了~不需要强制转换成sun的HttpUrlConnection
10 楼 zacry 2014-12-30  
写的不错,拜读!
9 楼 di1984HIT 2014-01-03  
才发现,原来这个问题啊。
8 楼 dreamoftch 2013-05-07  
谢了,这个有用
7 楼 qyc898 2011-11-15  
那你说的HTTP的第三方包在哪里下啊
6 楼 pig345 2008-04-25  
多段上传,可以考虑在server自己作些支持,不过有些麻烦的。
5 楼 skydream 2008-02-21  
多线程下载是因为http协议支持,可以获取一个文件的特定的部分,比如从100000个字节到200000个字节的内容,或者300000字节之后的所有内容。

多线程分段上载,这个http协议没有定义吧。你让谁来支持?
4 楼 ferreousbox 2008-02-21  
呵,这个是文件上传到服务器,不是下载,不知道各位看清楚没有.再有目前的浏览器虽然可以多线程下载,但是并不支持多线程分段上载的!
3 楼 jomper 2008-01-21  
抛出异常的爱 写道
多开几个url
每一个都用一个定长的
分别写到本地文本中去.
之后用工具把几个文件连在一起去.


典型的分段下载,用RandomAccessFile.
2 楼 抛出异常的爱 2008-01-21  
多开几个url
每一个都用一个定长的
分别写到本地文本中去.
之后用工具把几个文件连在一起去.
1 楼 jianrc 2008-01-21  
看似三种都不是好的解决办法.

相关推荐

    JAVA通过HttpURLConnection 上传和下载文件的方法

    JAVA通过HttpURLConnection上传和下载文件的方法 JAVA通过HttpURLConnection上传和下载文件的方法是非常有实用价值的,需要的朋友可以参考下。HttpURLConnection是一个Java类,用于从网络中读取数据或向网络中写入...

    Android HttpURLConnection.getResponseCode()错误解决方法

    导语:个人对网络连接接触的不多,在使用时自己发现一些问题,记录一下。... 解决方法: 方法1、网页返回内容不能是空; 方法2、不要用这个接口咯。 您可能感兴趣的文章:Android使用URLConnection提交请求的实现Androi

    HttpURLConnection文件下载\httpURLConnection文件下载

    在`downLoadByList()`方法中,遍历了`vDownLoad`和`vFileList`,获取每个URL和对应的本地文件名。然后调用`saveToFile()`方法来实际执行文件下载。这里使用了`try-catch`语句块来捕获可能的异常,如果下载失败,会...

    Android HttpUrlConnection json使用方法

    总结来说,Android中的HttpUrlConnection是处理网络请求的重要工具,它可以方便地进行JSON数据的POST和GET操作。通过理解HTTP请求方法、设置请求头、处理响应以及解析JSON,开发者可以有效地与服务器交换数据。在...

    HttpURLConnection servlet 多文件参数 断点上传

    `HttpURLConnection`提供了`setFixedLengthStreamingMode()`和`setChunkedStreamingMode()`方法,分别用于定长和不定长的流式上传。 在实际应用中,我们还需要考虑错误处理和重试机制,例如网络中断、超时等情况。...

    Httpurlconnection

    HttpURLConnection都提供了解决这些问题的方法,例如通过`setInstanceFollowRedirects(boolean)`控制自动重定向,或者使用`SSLSocketFactory`和`TrustManager`处理安全连接。 总的来说,HttpURLConnection是Java中...

    HttpURLConnection和简单的Android服务器交互

    相比于HttpClient,HttpURLConnection更易于管理和配置,且在Android API 23及以后版本中被推荐使用。 **请求过程** 1. **建立连接**:首先,我们需要获取到URL对象,并通过`openConnection()`方法创建一个...

    使用HttpURLConnection下载图片

    首先,我们需要创建一个`URL`对象,然后通过`openConnection()`方法获取`HttpURLConnection`实例。例如: ```java URL url = new URL("http://example.com/image.jpg"); HttpURLConnection connection = ...

    使用HttpUrlConnection实现上传文件 服务器端代码

    在Java编程环境中,当需要与Web服务器交互,例如上传文件时,`HttpURLConnection`是一个常见的选择,因为它提供了灵活且高效的方式。本篇文章将详细讲解如何使用`HttpURLConnection`实现文件上传,同时涉及到服务器...

    本示例使用HttpUrlConnection实现上传文件

    1. **创建连接**:使用`URL`对象构造一个`HttpURLConnection`实例,通常会通过`openConnection()`方法来实现。例如: ```java URL url = new URL("http://yourserver.com/upload"); HttpURLConnection connection...

    HttpURLConnection使用总结示例源码

    本篇文章将深入探讨HttpURLConnection的使用方法、特性以及一些关键的示例代码。 一、HttpURLConnection简介 HttpURLConnection继承自URLConnection,它提供了对HTTP协议的直接支持。相比于HttpClient,...

    AsyncTask结合HttpUrlConnection的例子

    你可以先创建`HttpURLConnection`对象,然后设置请求属性,接着执行`connect()`方法建立连接。之后根据请求方法不同,处理请求体数据。最后,通过`getInputStream()`或`getErrorStream()`读取响应。 示例代码可能...

    HttpURLConnection读取本地目录上传远程服务器

    1. **建立连接**:创建`URL`对象,表示本地文件或目录所在的URL,然后通过`openConnection()`方法获取`HttpURLConnection`实例。 2. **设置请求方法**:由于默认的请求方法是GET,我们需要调用`setRequestMethod(...

    HttpURLConnection用法详解

    HttpURLConnection是Java.net.URL的子类,它提供了对HTTP协议的支持,可以用来发送GET、POST和其他HTTP方法的请求。相比早期的Apache HttpClient库,HttpURLConnection具有更好的性能和更低的内存占用,因此现在更...

    远程连接服务器HttpURLConnection

    接下来,我们可以设置请求方法(如GET或POST),并启用输入/输出流以便读取和写入数据: ```java connection.setRequestMethod("POST"); // 或 "GET" connection.setDoOutput(true); // 对于POST请求,表示有数据要...

    HttpUrlConnection实例

    以上步骤提供了一个基本的框架,但实际应用中可能需要处理更多细节,如添加请求头(如`Content-Type: application/json`)、处理POST请求(包括设置输出流和写入请求体)、错误处理等。 在进行网络请求时,为了遵守...

    HttpURLConnection

    HttpURLConnection的demo,里面有网络请求get post 上传文件,下载文件,介绍HttpURLConnection这个类整体流程是怎样使用的,方便初学者学习

    HttpURLConnection调用.net WebService

    2. 连接关闭:调用完`HttpURLConnection`后,记得关闭输入流、输出流和连接,避免资源泄漏。 3. 安全性:如果涉及敏感数据传输,应考虑使用HTTPS。 4. 异步调用:为了不影响UI线程,通常在Android应用中我们会使用...

    android 联网请求的两种方式HttpURLConnection和HttpClient

    常见的联网请求方式有两种:HttpURLConnection和HttpClient。下面将详细讲解这两种方法,以及它们如何处理POST和GET请求。 **HttpURLConnection** HttpURLConnection是Java标准库提供的类,自Android 2.3(API级别9...

    使用HttpURLConnection

    在Java编程中,`HttpURLConnection`是用于处理HTTP请求的核心类,它位于`java.net`包下,是标准的Java API。这个类提供了一个接口,让...然而,理解`HttpURLConnection`的工作原理对于解决问题和优化性能仍然非常重要。

Global site tag (gtag.js) - Google Analytics