输出流出内存发送 HTTP 时的错误

2022-09-04 01:00:57

我正在尝试将大型视频/图像文件从本地文件系统发布到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)

答案 1

正在缓冲数据,以便它可以设置标头(根据 HTTP 规范)。HttpUrlConnectionContent-Length

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

另一种选择(imo是一个更好的选择)是使用Jakarta HttpClient。您可以在来自文件的请求中设置“实体”,连接代码将相应地设置请求标头。


编辑:没有评论说OP可以调用。我不知道这种方法;它是在1.5中添加的,从那时起我就没有使用过这个类。HttpURLConnection.setFixedLengthStreamingMode(long length)

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

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

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


答案 2
conn.setFixedLengthStreamingMode((int) new File(localpath).length());

对于缓冲,您可以将流覆盖到 BufferedOutputStreamBufferedInputStream 中。

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


推荐