java - 如何使用 angular.js 和 java 使用大文件逐块下载大文件
问题描述
我们有 web 应用程序,客户端在 agnular.js 中,服务器在 java spring 中。我正在研究从客户端下载此日志文件的功能,即 logs.tar。
目前我们正在使用blob下载。我们的问题是,如果这个日志大小变得非常大,超过 2GB,那么在流式传输时它会在应用程序内存上产生负载。所以我想要逐块下载大文件的方法,而不需要将整个 blob 加载到内存中。请提出出路。
服务器端 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();
}
...
}
解决方案
重要的是不要将文件读入内存,而是将流传递:
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);
}
}
...
}
考虑压缩,因为这将大大加快速度并减少服务器负载。应该研究分块、设置内容长度、放气压缩网络过滤器等。
推荐阅读
- bash - Mac Mojave 完全禁用了我的本地主机
- grep - Grep 重定向在 log.txt 中提取我想要的更多信息
- azure - 如果 count 参数设置为 0,则条件资源将被销毁
- commercetools - 为什么在禁用消息 HTTP REST API 时可以获取所有消息?
- kubernetes - 使用 Terraform 部署 Kubernetes 应用程序
- shell - 如何在没有硬编码变量的情况下移动项目
- sql - 使用scala从spark hadoop中的字符串中提取单词
- ios - iOS - 如何让一个单独的 UIView 控制 UISlider 的 thumbRect
- html - 如何在下拉数据切换上提供可点击的链接(到其他页面)?
- c# - 在 Web Api 中验证 AzureAD 令牌