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.
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
andResourceResponse
, 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 forPortletResponseUtil
. There are various sendFile methods that accept abyte[]
, 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 variableinput
in the question: If that's abyte[]
, 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.
Thanks for thoughts, finally I used
PortletResponseUtil.sendFile(...);
method and changedactionURL
toresponseURL
in.jsp
file. So that I implementedserveResource()
with above mentioned method. It seems that everything is working fine.Don't read a file once time.Use a buffer.