2016-05-12 2 views
1

Я работаю над приложением Spring-MVC, в котором пользователи могут загружать файлы. Пользователи могут нажать на приложение, которое запускает механизм загрузки.Spring, Java: загрузка потокового файла для устранения ошибок в памяти

Вчера, когда несколько загрузок и два из которых имели приблизительно 2 ГБ файлов, это вызвало ошибку из памяти (журнал ниже).

Чтобы избежать этой проблемы, один из способов решения этой проблемы представлял собой потоковое скачивание данных в кусках и обработку этих фрагментов только на уровне сервиса, а не на весь файл.

К сожалению, я не знаю, как двигаться дальше, любая помощь будет приятной. Если эта опция не может летать, вы можете найти рекомендации по решению этой проблемы.

Журнал ошибок: Код

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

Спасибо. :-)

Update

Новый механизм загрузки:

Контроллер:

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

Вы все читаете в памяти, не делайте этого ... Файлы Files.readAllBytes загружают весь «2Gb» в память. Просто не надо. Завершите загрузку и вместо первой загрузки, прочитайте ее часть по частям и поместите ее прямо. Вместо этого используйте 'Files.copy (Path, OutputStream)'. –

+0

Копия будет передавать его, а не загружать. Пожалуйста, прочитайте комментарий ... –

+0

@ M.Deinum: Извините за неправильное чтение. Я обновил код, а также попытался с Files.copy (путь, outputStream), работает красиво. Я опубликовал код как обновление в главном сообщении, можете ли вы проверить, есть ли какая-либо возможная оптимизация. Спасибо. –

ответ

2

Первая остановка загрузки содержимого всего в службе , так как там вы загружаете wh ole много содержимого файла в память.

Создайте метод, который создает 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(); 

} 

Там нет необходимости, чтобы сделать его более сложным IMHO.

+0

Помимо содержания, все одинаково. Большое спасибо. :-) –

+0

Некоторые браузеры требуют длины контента, другие отлично работают без него. –

+0

Уже добавлено, но мне пришлось отдать его, 'response.setContentLength ((int) Files.size (path));' –

Смежные вопросы