Java 发送HTTP时发生OutputStream OutOfMemory错误

Java 发送HTTP时发生OutputStream OutOfMemory错误,java,http,out-of-memory,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 =

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

}

您的问题是,当N>1时,您试图将X个视频字节修复为X/N字节的RAM

您需要将视频读入一个较小的缓冲区,然后边读边写,或者缩小文件或增加进程可用的内存


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

HttpUrlConnection正在缓冲数据,以便它可以设置
内容长度
标题(per)

如果目标服务器支持,另一种选择是使用“”传输。这将一次只缓冲一小部分数据。然而,并不是所有的服务都支持它(例如,AmazonS3不支持)

另一种选择(在我看来是更好的选择)是使用。您可以在来自文件的请求中设置“实体”,连接代码将适当地设置请求头


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

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

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

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

问题是HttpURLConnection类正在使用字节数组存储数据。你正在推的这段视频大概占用了比可用内存更多的内存。您在这里有几个选项:

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

  • >P>如果内存仍然用完了,你可能会考虑尝试另一个库来把不把数据全部存储在内存中的视频推到一起。ApacheCommonsHttpClient有这样一个特性。有关详细信息,请参阅此网站:。有关大文件的多部分表单上载,请参阅本节:


    除了基本的GET操作之外,内置的
    java.net
    HTTP东西不是很好。为此,建议使用。它可以让你做更直观的事情,比如:

    PutMethod put = new PutMethod(url);
    put.setRequestEntity(new FileRequestEntity(localFile, contentType));
    int responseCode = put.executeMethod();
    
    它取代了很多锅炉铭牌代码

    conn.setFixedLengthStreamingMode((int) new File(localpath).length());
    
    对于缓冲,您可以将流覆盖到BufferedOutputStream和BufferedInputStream中

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

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

    一些人(包括我)认为交叉发布是不礼貌的:尤其是当你甚至没有提到这一事实时。请不要对我批评你的代码的编辑感到生气。这比平均水平要好,但仍有改进的余地。所有非平凡的代码都可以。而且很容易搞乱异常处理:大约一周前,当我刚刚输入一个示例try/catch/finally而不让编译器检查时,我得到了一个当之无愧的-1。在这种情况下,也可以根据文件大小调用HttpURLConnection.setfixedlingthstreamingmode。这将导致HttpUrlConnection返回一个直接流,因为它不需要缓冲区来确定content length.nos是否正确。绝对没有必要使用分块请求或使用第三方库来实现这一点。在连接之前,只需使用HttpURLConnection上的文件长度调用setFixedLengthStreamingMode,getOutputStream将向套接字的输出流返回一个非缓冲直接包装器。@jarnbjo-我假设您是投票否决我和其他所有响应者的人。虽然你的评论很有价值,但把它们作为回应发表会更有建设性。@jarnbjo-你是对的。你没有任何建设性的补充。好吧,只要让你高兴就好。顺便说一句,你的上下投票率给我留下了深刻的印象。@jarnbjo-编辑了这篇文章并做了更多的解释,希望这能让你理解为什么“一行修改”(实际上是两行)绝对不是最简单的解决方案
    conn.setFixedLengthStreamingMode((int) new File(localpath).length());