Spring,Java:流文件下载,以避免内存不足错误

时间:2016-05-12 08:42:16

标签: java spring file spring-mvc download

我正在开发一个Spring-MVC应用程序,用户可以在其中下载文件。用户可以单击触发下载机制的附件。

昨天,当多次下载,其中两次有大约2 GB的文件时,会导致内存不足错误(日志如下)。

为了避免这个问题,解决这个问题的一种方法似乎是以块的形式传输下载数据,而只处理服务层中的那些块,而不是整个文件。

不幸的是,我不知道如何继续前进,任何帮助都会很好。如果此选项无效,请提供有关如何解决此问题的任何建议。

错误日志:

HTTP Status 500 - Handler processing failed; nested exception is java.lang.OutOfMemoryError: Direct buffer memory

type Exception report

message Handler processing failed; nested exception is java.lang.OutOfMemoryError: Direct buffer memory

description The server encountered an internal error that prevented it from fulfilling this request.

exception

org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.OutOfMemoryError: Direct buffer memory
    org.springframework.web.servlet.DispatcherServlet.triggerAfterCompletionWithError(DispatcherServlet.java:1303)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:977)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843)

控制器代码:

    @RequestMapping(value = "/download/attachment/{attachid}", method = RequestMethod.GET)
        public void getAttachmentFromDatabase(@PathVariable("attachid") int attachid,
    , HttpServletResponse response,) {

response.setContentType("application/octet-stream");
GroupAttachments groupAttachments = this.groupAttachmentsService.getAttachmenById(attachid);
response.setHeader("Content-Disposition", "attachment; filename=\"" + groupAttachments.getFileName() + "\"");
                            response.setContentLength(groupAttachments.getSendAttachment().length);
                            FileCopyUtils.copy(groupAttachments.getSendAttachment(), response.getOutputStream());
    response.flushBuffer();

    }

服务层:

@Override
    public GroupAttachments getAttachmenById(int attachId) {
        Person person = this.personService.getCurrentlyAuthenticatedUser();
        GroupAttachments groupAttachments = this.groupAttachmentsDAO.getAttachmenById(attachId);

        GroupMembers groupMembers = this.groupMembersService.returnMembersMatchingUsernameAccountId(person.getUsername(),
                groupAttachments.getGroupId());
        if (!(groupMembers == null)) {
            if (person.getUsername().equals(groupMembers.getMemberUsername())) {
                try {
                    Path path = Paths.get(msg + groupAttachments.getGroupId() + "/" +
                            groupAttachments.getFileIdentifier());
                    groupAttachments.setSendAttachment(Files.readAllBytes(path));
                    return groupAttachments;
                } catch (IOException ignored) {
                    this.groupAttachmentsDAO.removeAttachment(attachId);
                    return null;
                }
            }
            return null;
        } else {
            return null;
        }
    }

谢谢。 : - )

更新

新下载机制:

控制器:

 public ResponseEntity<byte[]> getAttachmentFromDatabase(@PathVariable("attachid") int attachid,
                                                    @PathVariable("groupaccountid") Long groupAccountId, @PathVariable("api") String api,
                                                    HttpServletResponse response,
                                                    @PathVariable("type") boolean type) {

 Path path = this.groupAttachmentsService.getAttachmentPathById(attachid);

        GroupAttachments groupAttachments = this.groupAttachmentsService.getAttachmentObjectOnlyById(attachid);
                        response.setContentType("application/octet-stream");
                        response.setHeader("Content-Disposition", "attachment; filename=\""+groupAttachments.getFileName()+"\"");
  try {
OutputStream outputStream = response.getOutputStream();

Files.copy(path,outputStream);
outputStream.flush();
outputStream.close();
response.flushBuffer();
}

服务层:

@Override
    public Path getAttachmentPathById(int attachId){
        Person person = this.personService.getCurrentlyAuthenticatedUser();
        GroupAttachments groupAttachments = this.groupAttachmentsDAO.getAttachmenById(attachId);

        GroupMembers groupMembers = this.groupMembersService.returnMembersMatchingUsernameAccountId(person.getUsername(),
                groupAttachments.getGroupId());
        if (!(groupMembers == null)) {
            if (person.getUsername().equals(groupMembers.getMemberUsername())) {
                try {
                    return Paths.get(msg + groupAttachments.getGroupId() + "/" +
                            groupAttachments.getFileIdentifier());
                } catch (Exception ignored) {
                    return null;
                }
            }
            return null;
        } else {
            return null;
        }
    }

1 个答案:

答案 0 :(得分:3)

首先停止在服务中加载整个内容,因为您正在将大量文件内容加载到内存中。

创建一个为Path构建GroupAttachments的方法,我会在GroupAttachments自己创建它。

public class GroupAttachments {

    public Path getPath() {
        return Paths.get(msg + getGroupId() + "/" + getFileIdentifier());
    }
}

然后在您的控制器中执行

@RequestMapping(value = "/download/attachment/{attachid}", method = RequestMethod.GET)
public void getAttachmentFromDatabase(@PathVariable("attachid") int attachid, HttpServletResponse response) {

  response.setContentType("application/octet-stream");
  GroupAttachments groupAttachments = this.groupAttachmentsService.getAttachmenById(attachid);
  Path path = groupAttachmetns.getPath(); // calculates the java.nio.file.Path  
  response.setHeader("Content-Disposition", "attachment; filename=\"" + path.getFileName() + "\"");
  response.setContentLength(Files.size(path);
  Files.copy(path, response.getOutputStream());
  response.flushBuffer();

}

没有必要让它变得更加复杂。