как загружать большие файлы по частям, используя большие файлы, используя angular.js и java

У нас есть веб-приложение с клиентом в agnular.js и сервером в java spring. Я работаю над функциональностью загрузки этого файла журнала, т.е. logs.tar с клиента.

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

Java-код на стороне сервера -

   public ResponseEntity<?> downloadLogs(HttpServletRequest request) {
        File file = preferencesService.downloadLogs();
        if (file != null) {
            FileInputStream inputStream;
            try {
                inputStream = new FileInputStream(file);
                byte[] content = FileCopyUtils.copyToByteArray(inputStream);

                String filename = "com-logs.tar";
                HttpHeaders responseHeaders = new HttpHeaders();
                responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);
                responseHeaders.add(HttpHeaders.CONTENT_TYPE, "application/octet-stream"

);

        return new ResponseEntity<byte[]>(content, responseHeaders, HttpStatus.OK);

        } catch (Exception e) {
            logger.error("Error while processing log file for download", e);
        }
    } else {
        logger.error("Failed to download logs");
    }
    return ResponseEntity.badRequest().build();
} 

Код Angular.js на стороне клиента —

this._service.downloadLogs().subscribe(
            success => {               
                var blb = new Blob([success], { 'type': "application/octet-stream" });
                if (window.navigator && window.navigator.msSaveOrOpenBlob) {
                    window.navigator.msSaveOrOpenBlob(blb, 'logs.tar');
                }
                else {
                    var link = document.createElement('a');
                    link.href = window.URL.createObjectURL(blb);
                    link.download = "logs.tar";
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                }
            });

Новый Java-код на стороне сервера -

public void downloadLogs(HttpServletResponse resonse) {
        File file = preferencesService.downloadLogs(id);       
        if (file != null) {
            try {
                resonse.setContentType("application/octet-stream");
                resonse.setHeader("Content-Disposition", "attachment;filename=" + file.getName());
                BufferedInputStream inStrem = new BufferedInputStream(new FileInputStream(file));
                BufferedOutputStream outStream = new BufferedOutputStream(resonse.getOutputStream());

                byte[] buffer = new byte[1024];
                int bytesRead = 0;
                while ((bytesRead = inStrem.read(buffer)) != -1) {
                    outStream.write(buffer, 0, bytesRead);
                }
                outStream.flush();
                inStrem.close();

            } 
            ...
        }

person questp    schedule 07.08.2018    source источник
comment
Рассмотрите также сжатие gzip (logs.tar.gz / GZipOutputStream). Это может быть сделано автоматически с помощью фильтра, если браузер подтверждает возможность выкачивания в заголовке запроса. Но просто загрузив файл . tar,gz (.tgz) тоже подойдет.   -  person Joop Eggen    schedule 07.08.2018
comment
Я не знаю Angular, но зачем использовать Angular для чего-то, с чем простой HTML и браузер могут справиться сами по себе? Я думаю, что это может вызвать большую нагрузку на браузер, если файл журнала большой.   -  person Christian Hujer    schedule 08.08.2018
comment
Код Spring на стороне сервера см. также в этом StackOverflow stackoverflow.com/questions/5673260/   -  person Christian Hujer    schedule 08.08.2018


Ответы (1)


Важно не читать файл в память, а передать поток дальше:

public ResponseEntity<?> downloadLogs(HttpServletRequest request) {
    File file = preferencesService.downloadLogs();
    if (file != null) {
        try (InputStream inputStream = Files.newInputStream(file.toPath())) {
            InputStreamResource inputStreamResource =
                    new InputStreamResource(new inputStream);
            HttpHeaders responseHeaders = new HttpHeaders();
            //responseHeaders.setContentLength(Files.size(file.toPath()));
            responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="
                    + filename);
            responseHeaders.add(HttpHeaders.CONTENT_TYPE, "application/octet-stream");
            return new ResponseEntity(inputStreamResource, responseHeaders, HttpStatus.OK);
        }
    }
    ...
}

Подумайте о сжатии, так как это значительно ускорит работу и снизит нагрузку на сервер. Следует обратить внимание на фрагментацию, настройку длины контента, сжатие веб-фильтров и т. д.

person Joop Eggen    schedule 07.08.2018
comment
Привет, Юп Эгген, спасибо за ответ ... будет ли мой клиентский код работать с этим, поскольку ваш код отправляет inputStreamResource в ResponseEntity, куда я отправлял контент.. ?? какие изменения мне нужно будет сделать на стороне клиента. - person questp; 08.08.2018
comment
Клиентская сторона независима. Отличие в том, что раньше все грузилось в память и потом стримилось клиенту, а не сразу стримилось клиенту. Без накладных расходов на память и задержки загрузки в память. - person Joop Eggen; 08.08.2018
comment
Привет, Юп Эгген, поскольку указанный вами код выдавал некоторые ошибки, я добавил новый код на стороне сервера в новый код Java на стороне сервера - ... . этот код работает, но каждый раз, когда я загружаю файл, он добавляет размер файла в память браузера, что является моей основной проблемой ... не могли бы вы помочь. - person questp; 08.08.2018
comment
Логи загрузки сервера в порядке. Клиентская сторона AngularJS действительно неоптимальна. Я не могу сказать, как это решить. Разрешить загрузку браузера без JavaScript, казалось бы, лучше всего. На стороне сервера немного поможет упаковка вывода в GZipOutputStream (.tar.gz). См. также stackoverflow.com/questions/24080018/ (не кажется полезным) - person Joop Eggen; 08.08.2018