发送HTTP时OutputStream OutOfMemoryError

时间:2010-01-17 18:18:51

标签: java http out-of-memory

我正在尝试将大型视频/图像文件从本地文件系统发布到http路径,但是在一段时间后我遇到内存不足错误...

这是代码

public boolean publishFile(URI publishTo, String localPath) throws Exception {
    InputStream istream = null;
    OutputStream ostream = null;
    boolean isPublishSuccess = false;

    URL url = makeURL(publishTo.getHost(), this.port, publishTo.getPath());
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();


    if (conn != null) {

        try {

            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setRequestMethod("PUT");
            istream = new FileInputStream(localPath);
            ostream = conn.getOutputStream();

            int n;
            byte[] buf = new byte[4096];
            while ((n = istream.read(buf, 0, buf.length)) > 0) {
                ostream.write(buf, 0, n); //<--- ERROR happens on this line.......???
            }

            int rc = conn.getResponseCode();

            if (rc == 201) {
                isPublishSuccess = true;
            }

        } catch (Exception ex) {
            log.error(ex);
        } finally {
            if (ostream != null) {
                ostream.close();
            }

            if (istream != null) {
                istream.close();
            }
        }
    }

    return isPublishSuccess;

}

HEre是我得到的错误......

Exception in thread "Thread-8773" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2786)
    at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:94)
    at sun.net.www.http.PosterOutputStream.write(PosterOutputStream.java:61)
    at com.test.HTTPClient.publishFile(HTTPClient.java:110)
    at com.test.HttpFileTransport.put(HttpFileTransport.java:97)

6 个答案:

答案 0 :(得分:13)

HttpUrlConnection正在缓冲数据,以便它可以设置Content-Length标题(每HTTP spec)。

另一种方法是,如果目标服务器支持,则使用“chunked”传输。这将一次只缓冲一小部分数据。但是,并非所有服务都支持它(例如,Amazon S3不支持)。

另一个替代方案(并且更好的方法)是使用Jakarta HttpClient。您可以在文件的请求中设置“实体”,连接代码将适当地设置请求标头。


编辑: nos 评论说OP可以调用HttpURLConnection.setFixedLengthStreamingMode(long length)。我没有意识到这种方法;它是在1.5中添加的,从那以后我没有使用过这个类。

但是,建议使用Jakarta HttpClient,原因很简单,它减少了OP必须维护的代码量。代码是样板,但仍有可能出错:

  • OP正确处理在输入和输出之间复制的循环。通常当我看到这样的例子时,海报要么没有正确检查返回的缓冲区大小,要么继续重新分配缓冲区。恭喜,但您现在必须确保您的继任者得到尽可能多的关注。
  • 异常处理不是很好。是的,OP记得关闭finally块中的连接,并再次祝贺。除了close()调用之一可以抛出IOException,保持另一个不执行。并且整个方法抛出Exception,因此编译器不会帮助捕获类似的错误。
  • 我计算了31行代码来设置和执行响应(不包括响应代码检查和URL计算,但包括try / catch / finally)。使用HttpClient,这将在半打LOC的范围内。

即使OP完全编写了这段代码,并将其重构为类似于Jakarta Commons IO中的方法, s /他也不应该这样做。此代码已由其他人编写和测试。我知道浪费我的时间来重写它,并怀疑它也浪费了OP的时间。

答案 1 :(得分:3)

conn.setFixedLengthStreamingMode((int) new File(localpath).length());

对于缓冲,您可以将流覆盖到 BufferedOutputStream BufferedInputStream

您可以在那里找到分组上传的好例子:gdata-java-client

答案 2 :(得分:2)

问题是HttpURLConnection类使用字节数组来存储数据。据推测,你正在推动的视频占用的内存比可用内容多。你有几个选择:

  1. 增加应用程序的内存。您可以使用-Xmx1024m选项为您的应用程序提供1GB内存。这将增加您可以存储在内存中的数据量。

  2. 如果内存不足,您可能需要考虑尝试使用另一个库来推送不会将数据全部存储在内存中的视频。 Apache Commons HttpClient具有这样的功能。有关详情,请访问此网站:http://hc.apache.org/httpclient-3.x/features.html。有关大文件的多部分表单上载,请参阅此部分:http://hc.apache.org/httpclient-3.x/methods/multipartpost.html

答案 3 :(得分:2)

除了基本的GET操作之外,内置的java.net HTTP内容不是很好。建议使用Apache Commons HttpClient。它可以让你做更直观的事情:

PutMethod put = new PutMethod(url);
put.setRequestEntity(new FileRequestEntity(localFile, contentType));
int responseCode = put.executeMethod();

取代了很多锅炉铭牌代码。

答案 4 :(得分:1)

HttpsURLConnection#setChunkedStreamingMode(1024 * 1024 * 10); // 10MB块 这可确保任何文件(任何大小)都通过https连接进行流式传输,无需内部缓冲。当文件大小或内容长度未知时,应使用此方法。

答案 5 :(得分:-1)

你的问题是你正在尝试将X视频字节修复为X / N字节的RAM,当N> 1.

您需要将视频读入较小的缓冲区并随时将其写出,或者将文件缩小或增加进程可用的内存。

检查堆大小。如果您采用默认值,则可以使用-Xmx来增加它。