Spring Boot+Angular如何实现文件上传下载进度观测
Angular 配合 Spring Boot 实现文件传输进度的方案说明
reportProgress 方案的适用范围
这个配置不是只支持文件上传场景,下载场景同样可以原生支持,不需要额外改造:
- 上传场景:发起POST/PUT等带请求体的请求传输文件时,设置
reportProgress: true+observe: 'events',订阅请求后筛选HttpEventType.UploadProgress类型的事件,就能通过事件携带的loaded(已发送字节数)、total(总字节数)计算上传进度。 - 下载场景:发起GET请求拉取文件流时,同样开启
reportProgress: true,将responseType设为blob避免Angular默认按JSON解析响应,之后筛选HttpEventType.DownloadProgress类型的事件,即可拿到已下载字节数计算进度,和上传的写法几乎一致。
底层实现逻辑
这里先纠正一个常见误解:这个方案完全不需要客户端轮询,也不需要Spring Boot后端做任何进度查询逻辑,和服务端基本无关。
所有进度事件来自浏览器底层的HTTP客户端能力(Angular HttpClient默认基于XMLHttpRequest实现,即便切换到Fetch API也有对应的进度回调标准):
- 上传进度:浏览器向TCP连接写入请求体数据时,每成功写入一块数据就会触发原生的
upload.onprogress回调,已发送字节数是浏览器本地统计的,不需要向服务端询问已接收数据量。 - 下载进度:浏览器从TCP连接读取服务端返回的响应体数据时,每接收到一块数据就会触发原生的
onprogress回调,已接收字节数同样是本地统计的,不需要额外发请求。
注意:事件返回的
total值取自HTTP响应头的Content-Length字段,如果Spring Boot接口返回文件时没有手动设置该头,或者开启了分块传输(Transfer-Encoding: chunked),total会返回0,无法计算准确百分比。这种情况只需要在下载接口里给response设置Content-Length为文件实际大小即可,不需要其他改造。
给两个最小实现的代码片段参考:
// Angular 端带进度的文件下载示例 this.http.get('/api/file/download', { reportProgress: true, observe: 'events', responseType: 'blob' }).subscribe(event => { switch (event.type) { case HttpEventType.DownloadProgress: const progress = Math.round(100 * event.loaded / (event.total || 1)); console.log(`当前下载进度:${progress}%`); break; case HttpEventType.Response: // 下载完成,处理blob对象,比如触发浏览器保存 break; } });
// Spring Boot 端文件下载接口核心配置示例 @GetMapping("/api/file/download") public void downloadFile(HttpServletResponse response) throws IOException { File targetFile = new File("/path/to/your/file"); // 必须设置Content-Length,否则前端拿不到总字节数 response.setContentLengthLong(targetFile.length()); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); // 后续将文件流写入response即可 }
替代方案对比
不存在绝对最优的方案,根据业务场景选即可:
- 原生
reportProgress方案:
优点是零额外后端开发量,没有额外的连接/请求开销,实现成本极低,能覆盖90%以上只需要传输层进度的普通文件上传/下载场景,是这类场景的首选。
缺点是只能感知字节传输的进度,无法感知服务端的业务处理进度——比如上传完大压缩包后,服务端需要解压、校验、入库的阶段,这个方案只会显示进度100%,等待接口最终返回。 - Websocket/SSE 主动推送方案:
适合需要感知全链路业务进度的场景,比如大文件分片上传的分片校验、存储进度,或者文件接收完成后的转码、解析、入库等非传输阶段的进度。实现时文件传输本身依然走HTTP接口,服务端在每个处理节点通过长连接主动向前端推送进度即可。
缺点是需要额外维护长连接的生命周期,后端需要做请求标识和会话的绑定,开发和运维成本更高,大流量场景下长连接的资源开销也远高于普通HTTP请求,只适合复杂业务进度场景,不适合用来做单纯的传输进度统计。 - 轮询查询方案:
即客户端定时发请求向服务端查询已处理的数据量,这个方案实时性受轮询间隔影响,还有额外的请求开销,除非是极特殊的兼容性场景,否则现在基本不推荐使用。
内容的提问来源于stack exchange,提问作者Luk
相关产品推荐
相关产品推荐

