【问题标题】:How to fix an error `Java heap space` when downloading large files (GB) using Liferay使用 Liferay 下载大文件 (GB) 时如何修复错误“Java 堆空间”
【发布时间】:2017-05-24 22:27:46
【问题描述】:

我正在使用此代码将 Liferay (6.2) 上的服务器上的现有文件下载到本地电脑:

`

    File file = getFile(diskImage.getImageType(), diskImage.getId());

    HttpServletRequest httpReq = PortalUtil.getHttpServletRequest(request);
    HttpServletResponse httpResp = PortalUtil.getHttpServletResponse(response);

    httpResp.setContentType("application/octet-stream");
    httpResp.setHeader("Content-Transfer-Encoding", "binary");
    httpResp.setHeader("Content-Length", String.valueOf(file.length()));
    httpResp.setHeader("Content-Disposition", "attachment; filename=" + file.getName());

    try (InputStream input = new FileInputStream(file)) {
        ServletResponseUtil.sendFile(httpReq, httpResp, file.getName(), input, "application/octet-stream");
    } catch (Exception e) {
        throw new FilesManagerException(e);
    }
}

`

此代码仅适用于小文件。但是下载大文件(cca 2GB)会抛出javax.portlet.PortletException: Error occurred during request processing: Java heap space

如何修复此代码以使其也适用于较大的文件? 我想合适的方法是对大文件使用某种缓冲区,我尝试了一下,但之后即使是较小的文件也不起作用。

【问题讨论】:

  • 使用命令行选项 -Xmx 运行 Java,该选项设置堆的最大大小。
  • 是否可以使用某种实现而不是此设置来做到这一点?堆大小更改是否有任何负面影响?
  • 因为我不知道如何在远程 Liferay 服务器上使用它。我只是创建了部署在服务器上的.war 文件。

标签: java download liferay httprequest


【解决方案1】:

首先:我假设您在渲染方法中执行此操作 - 这是完全错误的。这迟早会中断,因为您无法控制输出流:当您的 portlet 开始呈现时,它可能已经提交并将数据传输到浏览器。在渲染中,您总是必须生成 portlet 的 HTML 代码。

相反,您应该转到 portlet 的资源服务阶段。使用ResourceRequestResourceResponse,您对设置mimetypes 的支持与使用HttpServletResponse 非常相似。

正是出于这个原因,ServletResponseUtil 确实是寻找错误的地方。如果你使用 Liferay 的任何东西,你应该寻找PortletResponseUtil。有多种 sendFile 方法可以接受 byte[],其他的则接受流或文件。我建议尝试这些,如果它们仍然失败,请查看您最终的实现。在最坏的情况下,不要使用任何 Util 方法。将内容从一个流复制到另一个流还不错。 (实际上,您对问题中的变量input 的静态类型一无所知:如果那是byte[],这就是您的解决方案)

如果确实纯流传输确实将整个文件读入内存,您可能想向 Liferay 提出问题,但您的快速修复(如果这确实是一个错误)将是自己复制数据。

【讨论】:

    【解决方案2】:

    感谢大家的意见,最后我用PortletResponseUtil.sendFile(...);的方法,在.jsp文件里把actionURL改成了responseURL。所以我用上述方法实现了serveResource()。似乎一切正常。

    【讨论】:

      【解决方案3】:

      ServletResponseUtil.sendFile(httpReq, httpResp, file.getName(), input, "application/octet-stream");这是什么?

      不要一次读取文件。使用缓冲区。

      response.reset();
      response.setContentType("application/x-download");
      response.addHeader("Content-Disposition","attachment;filename="+new String(filename.getBytes(),"utf-8"));
      response.addHeader("Content-Length",""+file.length());
      OutputStream toClient=new BufferedOutputStream(response.getOutputStream());
      response.setContentType("application/octet-stream");
      byte[] buffer=new byte[1024*1024*4];
      int i=-1;
      while((i=fis.read(buffer))!=-1){
        toClient.write(buffer,0,i);
      }
      fis.close();
      toClient.flush();
      toClient.close();
      

      【讨论】:

      • 不。我的意思是我不使用 ServletResponseUtil。
      猜你喜欢
      • 1970-01-01
      • 2013-08-17
      • 1970-01-01
      • 2022-01-23
      • 2013-06-28
      • 2019-08-15
      • 2012-04-16
      • 2016-01-21
      • 2020-03-01
      相关资源
      最近更新 更多