How to fix an error `Java heap space` when downloa

2019-03-04 13:17发布

I am using this code to download an existing file from the server on Liferay (6.2) into a local pc:

`

    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);
    }
}

`

This code works fine only for small files. But downloading large files (cca 2GB) throws javax.portlet.PortletException: Error occurred during request processing: Java heap space.

How to fix this code so it works properly for larger files as well? I guess that the suitable approach would be to use some kind of a buffer for large files and I try it but it wouldn't work even for the smaller files afterwards.

3条回答
混吃等死
2楼-- · 2019-03-04 13:59

First of all: I'm assuming you're doing this in a render method - and this is just plain wrong. Sooner or later this will break, because you don't have control over the output stream: It might already be committed and transmit data to the browser when your portlet starts to render. In render you always must generate a portlet's HTML code.

Instead, you should go to the resource serving phase of a portlet. With the ResourceRequest and ResourceResponse, you have a very similar support for setting mimetypes as with HttpServletResponse.

And for exactly that reason, ServletResponseUtil is indeed the wrong place to look for. If you use anything from Liferay, you should look for PortletResponseUtil. There are various sendFile methods that accept a byte[], others accept a stream or a file. I'd recommend to try these, if they still fail, look at the implementation you are ending up with. In the worst case, just don't use any of the Util methods. Copying content from one stream to another is not too bad. (Actually, you give no clue about the static type of your variable input in the question: If that's a byte[], there's your solution)

You might want to file an issue with Liferay, if indeed the pure stream-transfer does read the whole file into memory, but your quickfix (in case this is indeed a bug) would be to copy the data yourself.

查看更多
迷人小祖宗
3楼-- · 2019-03-04 14:12

Thanks for thoughts, finally I used PortletResponseUtil.sendFile(...); method and changed actionURL to responseURL in .jsp file. So that I implemented serveResource()with above mentioned method. It seems that everything is working fine.

查看更多
甜甜的少女心
4楼-- · 2019-03-04 14:12

ServletResponseUtil.sendFile(httpReq, httpResp, file.getName(), input, "application/octet-stream"); what's this?

Don't read a file once time.Use a buffer.

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();
查看更多
登录 后发表回答