编写下载服务器。 第一部分:始终流式传输,永远不要完全保留在内存中

下载各种文件(文本或二进制文件)是每个企业应用程序的生死攸关的事情。 PDF文档,附件,媒体,可执行文件,CSV,超大文件等。几乎每个应用程序迟早都必须提供某种形式的下载。 下载是通过HTTP来实现的,因此完全包含此协议并充分利用它很重要。 特别是在面向Internet的应用程序中,诸如缓存或用户体验之类的功能值得考虑。 本系列文章提供了实现各种下载服务器时可能要考虑的各个方面的列表。 请注意,我避免使用“ 最佳实践 ”一词,这些只是我认为有用的准则,但不一定总是适用。

最大的可伸缩性问题之一是在流传输之前将整个文件加载到内存中。 将完整文件加载到byte[]以便稍后从Spring MVC控制器返回它,这是无法预测的,并且无法缩放。 服务器将消耗的内存量与并发连接数乘以平均文件大小成线性关系,而您实际上并不想太依赖这些因素。 将文件内容直接从服务器逐字节流式传输到客户端(使用缓冲)非常容易,实际上有很多技术可以实现。 最简单的一种是手动复制字节:

@RequestMapping(method = GET)
public void download(OutputStream output) throws IOException {try(final InputStream myFile = openFile()) {IOUtils.copy(myFile, output);}
}

您的InputStream甚至不必缓冲, IOUtils.copy()会解决这个问题。 但是,此实现相当底层,并且很难进行单元测试。 相反,我建议返回Resource

@RestController
@RequestMapping("/download")
public class DownloadController {private final FileStorage storage;@Autowiredpublic DownloadController(FileStorage storage) {this.storage = storage;}@RequestMapping(method = GET, value = "/{uuid}")public Resource download(@PathVariable UUID uuid) {return storage.findFile(uuid).map(this::prepareResponse).orElseGet(this::notFound);}private Resource prepareResponse(FilePointer filePointer) {final InputStream inputStream = filePointer.open();return new InputStreamResource(inputStream);}private Resource notFound() {throw new NotFoundException();}
}@ResponseStatus(value= HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {
}

创建了两个抽象以使Spring控制器与文件存储机制脱钩。 FilePointer是一个文件描述符,与该文件的获取位置FilePointer 。 目前,我们使用一种方法:

public interface FilePointer {InputStream open();//more to come}

open()允许读取实际文件,无论它来自何处(文件系统,数据库BLOB,Amazon S3等)。我们将逐步扩展FilePointer以支持更多高级功能,例如文件大小和MIME类型。 查找和创建FilePointer的过程由FileStorage抽象控制:

public interface FileStorage {Optional<FilePointer> findFile(UUID uuid);
}

流式传输使我们能够处理数百个并发请求,而不会显着影响内存和GC( IOUtils仅分配了一个小缓冲区)。 顺便说一句,我正在使用UUID来识别文件,而不是名称或其他形式的序列号。 这使得猜测单个资源名称变得更加困难,因此更加安全(晦涩)。 下一篇文章将对此进行更多介绍。 有了此基本设置,我们可以可靠地为大量并发连接提供服务,而对内存的影响最小。 请记住,Spring框架中的许多组件和其他库(例如servlet过滤器)可能会在返回完整响应之前对其进行缓冲。 因此,进行集成测试以下载巨大的文件(以数十个GiB格式)并确保应用程序不会崩溃非常重要。

编写下载服务器

  • 第一部分:始终流式传输,永远不要完全保留在内存中
  • 第二部分:标头:Last-Modified,ETag和If-None-Match
  • 第三部分:标头:内容长度和范围
  • 第四部分:有效地实现HEAD操作
  • 第五部分:油门下载速度
  • 第六部分:描述您发送的内容(内容类型等)
  • 这些文章中开发的示例应用程序可在GitHub上找到。

翻译自: https://www.javacodegeeks.com/2015/06/writing-a-download-server-part-i-always-stream-never-keep-fully-in-memory.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/359437.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++输入cin详解

C输入cin详解 输入原理&#xff1a; 程序的输入都建有一个缓冲区&#xff0c;即输入缓冲区。一次输入过程是这样的&#xff0c;当一次键盘输入结束时会将输入的数据存入输入缓冲区&#xff0c;而cin函数直接从输入缓冲区中取数据。正因为cin函数是直接从缓冲区取数据的&#xf…

时间序列的截尾和拖尾_R语言:时间序列(一)

01 解决什么问题在社会活动中经常可见按照时间顺序记录下来的随机事件观察值&#xff0c;例如每年死亡人数序列&#xff0c;每年糖尿病发病人数序列&#xff0c;医院门诊每日诊治病例数序列。这类数据的特性是相邻时间点的观察值之间具有明显的相关性&#xff0c;这一特性不同于…

ulimit小结

1. limits是一个进程的资源&#xff0c;会被子进程继承2. soft limit -S, hard limits -Hhard limits只能被root用户修改&#xff0c;启动的时候会加载配置/etc/security/limits.confsoft limits可以被任何用户修改&#xff0c;但不能超过hard limits3. 在linux下&#xff0c;每…

JVM崩溃时:如何调查最严重错误的根本原因

当应用程序崩溃时&#xff0c;您可以学到什么&#xff1f; 我认为&#xff0c;“后见之明是20 /”是最喜欢的短语之一托马斯罗梅尔 &#xff0c;工程ZeroTurnaround的副总裁。 好吧&#xff0c;我实际上不确定在他的短语中占什么位置&#xff0c;但是我已经听过他几次说了。 鉴…

常用个人密码管理软件

http://www.williamlong.info/archives/3100.html转载于:https://www.cnblogs.com/svennee/p/4099358.html

查看网口命令_20个常用Linux命令

今天总结几个非常常用的Linux命令,其中有几个在面试中很可能问相关命令的原理,比如后台运行命令。希望对大家有所帮助,最好自己去尝试在Linux操作系统中实践一下。 1、查看目录以及权限 在windows中,使用dir查看当前目录中文件。在Linux中使用ls(list)查看当前目录文件。 w…

爱摘苹果的小明

描述小明家的院子里有一棵苹果树&#xff0c;每到秋天树上就会结出10个苹果。苹果成熟的时候&#xff0c;小明就会跑去摘苹果。小明有个30厘米高的板凳&#xff0c;当她不能直接用手摘到苹果的时候&#xff0c;就会踩到板凳上再试试。现在已知10个苹果到地面的高度&#xff0c;…

中统计字符串长度的函数_SQL Server中的字符串分割函数

您是否知道从SQL Server 2016开始&#xff0c;系统就内置STRING_SPLIT函数&#xff0c;该函数用于将字符串分隔的变量拆分为一个可用列表。 对于经常需要分割字符串的技术人员&#xff0c;建议您查看此功能。 STRING_SPLIT是一个表值函数&#xff0c;它返回由定界符分隔的字符串…

JBoss BPM Suite快速指南–将外部数据模型导入BPM项目

您正在从事一个大型项目&#xff0c;在企业中开发规则&#xff0c;事件和流程以满足关键业务需求。 部分要求指出&#xff0c;某个业务部门将提供您的数据模型供您利用。 不会在JBoss BPM Suite数据建模器中设计此数据模型&#xff0c;但是在从业务中心仪表板处理规则&#x…

Android项目笔记【项目管理统计图app】:使用github上的cardslib开源项目实现CardView(1)...

因为项目中用到第三级菜单&#xff0c;我们原有的界面框架已经不适用于该项目&#xff0c;Android L出了新的cardview设计&#xff0c;爬了下github发现有些高手已经把card整合为更方便调用的类库了&#xff0c;我这个项目就准备试用一下其中的一个开源项目cardslib &…

卸载 流程_一款适合于windows端的卸载神器 彻底清理残留软件

今天给大家介绍的是一款适合于Windows端的软件卸载神器---Uninstall&#xff0c;可以彻底清理残留软件。它的卸载流程是这样的&#xff0c;首先会使用软件本身的默认卸载程序进行卸载&#xff0c;卸载完成后再次扫描软件残留的一些残余文件及注册表之类的&#xff0c;可以完美的…

key value vue 输出_vue注意事项总结(一)

1.只有当vue实例被创建时data中存在的属性才是响应式的&#xff1a;如果你知道你会在晚些时候需要一个属性&#xff0c;但是一开始它为空或不存在&#xff0c;那么你仅需要设置一些初始值。2.不要在选项属性或回调上使用箭头函数&#xff1a;比如&#xff1a;created: () > …

netif_start_queue/netif_wake_queue/netif_stop_queue

在网卡驱动中&#xff0c;内核为发送数据包的流量控制提供了几个主要的函数&#xff0c;用来在驱动程序和内核之间传递流控信息。 主要有4个&#xff1a; 1】netif_start_queue 启动接口传输队列 2】netif_wake_queue 通知网络系统可以再次开始传输数据包&#xff1b;并启动接…

编写下载服务器。 第四部分:有效地实现HEAD操作

HEAD是一个经常被遗忘的HTTP方法&#xff08;动词&#xff09;&#xff0c;其行为类似于GET&#xff0c;但不会返回正文。 您使用HEAD来检查资源的存在&#xff08;如果不存在&#xff0c;它应该返回404&#xff09;&#xff0c;并确保您的缓存中没有陈旧的版本。 在这种情况下…

【grunt整合版】30分钟学会使用grunt打包前端代码

http://www.itnose.net/detail/6009394.html转载于:https://www.cnblogs.com/zifeiyu/p/4106585.html

十三水算法php_基于PHP+Redis令牌桶限流

一 、场景描述在开发接口服务器的过程中&#xff0c;为了防止客户端对于接口的滥用&#xff0c;保护服务器的资源&#xff0c; 通常来说我们会对于服务器上的各种接口进行调用次数的限制。比如对于某个 用户&#xff0c;他在一个时间段&#xff08;interval&#xff09;内&…

ECSHOP如何增加红包序列号字符

ECSHOP系统线下发放红包时系统生成的红包序列号是在10000的基础上增加四位随机数字。如果当我们要发放大额度红包的时候&#xff0c;这样的序列号规 则难免给人不安全的感觉&#xff0c;万一有无聊的人&#xff0c;蒙几个红包序列号出来&#xff0c;那就亏大了&#xff0c;因为…

Java REST JAX-RS 2.0 –如何处理日期,时间和时间戳记数据类型

无论是X-Form-Urlencoded还是JSON HTTP发布到REST资源端点&#xff0c;对于与日期或时间相关的数据都没有特定的“数据类型”。 大多数开发人员会将这些数据发布为“字符串”&#xff0c;或者只是将它们转换为Unix时间戳值&#xff08;例如1435061152&#xff09;。 但是&#…

html中给div设置的属性怎么样才能拿得到_HTML与CSS结合的三种方式:优先级比较...

所谓实践出真知&#xff0c;只有自己动手去做了&#xff0c;才能得到正确的结论。首先我们看看三种结合方式&#xff1a;通过link标签引入外部css文件通过style标签通过style属性很长一段时间我受这个一段话影响&#xff1a;在html文件中&#xff0c;代码的执行顺序是从外到内&…

Quartz作业调度框架及时间表达式的含义和语法

Quartz 是一个开源的作业调度框架&#xff0c;它完全由 Java 写成&#xff0c;并设计用于 J2SE 和 J2EE 应用中。它提供了巨大的灵活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。本系统结合通过 Spring 来集成 Quartz 。 Quartz 下载地址 &#x…