Springboot⽂件下载实现和原理分析
Springboot⽂件下载实现和原理分析
需求
1. 客户端发送请求,可以下载服务端指定⽂件
2. ⽆论什么⽂件,不允许浏览器⾃动解析,必须作为附件下载
分析
1. 采⽤springboot实现⽂件下载,本质上使⽤的也是javaEE的Servlet+Tomcat技术
2. 下载⽂件的本质是获取⽂件的读取流,在服务端需要将⽂件内容写⼊到Response的OutputStream中(注意这个写⼊流不需要
flush)
3. 为了防⽌浏览器解析,需要在响应头中,将⽂件类型设置为附件。
代码
/**
java下载过程* ⽂件下载的controller
*/
@GetMapping("/download")
public void downloadFile(HttpServletResponse response)throws IOException {
downloadFileService.download(response);
}
/**
* ⽂件下载的service代码
*/
public void download(HttpServletResponse response)throws IOException {
/
**
* 1、获取⽂件读取流
* 2、获取response写⼊流
* 3、将⽂件读取流的数据写⼊到写⼊流中
*/
File file =new File("d:\\01.jpg");
// 在常规的HTTP应答中,Content-Disposition 响应头指⽰回复的内容该以何种形式展⽰,
// 是以内联的形式(即⽹页或者页⾯的⼀部分),还是以附件的形式下载并保存到本地。
做一个web项目需要什么技术// fileName不是必须的,⽤于作为⽂件下载之后的默认名称
response.setHeader("content-disposition","attachment;filename="+ Name());
try(FileInputStream fileInputStream =new FileInputStream(file)){
ServletOutputStream outputStream = OutputStream();
byte[] bytes =new byte[1024*8];
int readBytes =0;
while((readBytes = ad(bytes))!=-1){
outputStream.write(bytes,0, readBytes);
}
}
}
下载过程Tomcat源码分析
通过http实现⽂件下载的原理是怎样的?
1. Servlet下载⽂件的核⼼代码是从某个输⼊流中读取数据,写到response的输出流中
ServletOutputStream outputStream = OutputStream();
byte[] bytes =new byte[1024*8];
async和await的区别int readBytes =0;
while((readBytes = ad(bytes))!=-1){
outputStream.write(bytes,0, readBytes);
}
2. 从上⾯的代码中⾃然就带出第⼀个问题:输出流的数据⽬的地是哪⾥?数据被写到哪⾥去了?接下来通过debug跟write⽅法的源码来
分析这个问题:
//⾸先调⽤write⽅法
outputStream.write(bytes,0, readBytes);
//然后调⽤org.tor.CoyoteOutputStream的write⽅法
ob.write(b, off, len);
//然后调⽤org.tor.OutputBuffer的⽅法
writeBytes(b, off, len);
//进⼀步调⽤org.tor.OutputBuffer的⽅法
append(b, off, len);//Add data to the buffer
//当达到⼀定条件时,将数据写到客户端的output中
realWriteBytes(ByteBuffer.wrap(src, off, limit));
//再进⼀步调⽤http11.filters.ChunkedOutputFilter
doWrite(ByteBuffer chunk)
buffer.doWrite(chunk);
工作流名词解释//在进⼀步调⽤at.util.NioEndpoint
doWrite(boolean block, ByteBuffer from)
手机网页视频加载不出来//继续调⽤at.util.NioSelectorPool,到这⼀步,已经快到了NIO的层⾯了
write(ByteBuffer buf, NioChannel socket, Selector selector,long writeTimeout)
//继续调⽤at.util.NioBlockingSelector
write(ByteBuffer buf, NioChannel socket,long writeTimeout)
//进⼀步调⽤at.util.NioChannel
socket.write(buf)
//再进⼀步调⽤java.nio.channels.SocketChannel
sc.write(src);//此时就调⽤到了java的nio包,这部分的源码好像就不开放了,只能看到反编译的内容
1. 通过以上源码分析可以推断,数据从原始⽂件到客户端,从最上层的outputStream的write⼀直调⽤到socket的write,最底层
的实现还是socket通信。⽽且数据不是⼀次性发送完成的,⽽是分成多个数据包发送,每次数据会⾸先缓存到io buffer中,当buffer达到临界值,就会调⽤socket的⽅法,把数据发送到客户端。
2. 为了从效果上更加明显地验证数据的分包传输,可以直接把断点打在outputStream的write⽅法上,可以发现每执⾏⼏次write
⽅法,客户端下载的临时⽂件就会变⼤。因此,第⼀个问题可以有⽐较靠谱的答案了。那就是outputStream的⽬标是io
buffer,最终分批次通过socket写到客户端。
⽂件下载的buffer原理【⾼级】
通过分析ServletResponse下载⽂件的过程,可以得出以下⾮常有意思的结论:
1. 定理1:⽂件下载的本质是⽹络数据传输,虽然表现为io流的⽅式,但是逐层跟源码可以发现,response io流的底层还是socket通
oracle10g下载教程信。
2. 定理2:response的outputStream数据实际上是先写⼊到io buffer中,达到临界条件时,数据通过socket写到客户端去,如此循
环,实现分批次传输。
3. 定理3:⽂件下载不是⼀次性把⼀个⽂件写出去,⽽是按照buffer分批次传输,是⼀个流式的过程。
1. 推论1:⽆论下载多⼤的⽂件,都不会导致服务内存溢出
2. 推论2:由于下载⽂件的过程是按buffer分批次传输的,因此⽤中间服务代理底层存储不会导致较⼤的性能损失。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论