搭建自己的下载文件服务器
文件的content-type
客户端向服务器端请求一个文件,服务器端在返回的HTTP头中会包含一个content-type的内容,这个content-type表示的是返回的文件类型。这个类型应该怎么确认呢?
一般来说,文件类型是根据文件的的扩展名来确认的,根据 RFC 4288的规范,所有的网络媒体类型都必须注册。apache也提供了一个文件MIME type和扩展名的映射关系表。
因为文件类型比较多,我们看几个比较常用到的类型如下:
JDK提供了一个MimetypesFileTypeMap的类,这个类提供了一个getContentType方法,可以根据请求的文件path信息,来推断其MIME type类型:
private static void setContentTypeHeader(HttpResponse response, File file) {MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();response.headers().set(HttpHeaderNames.CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));}
客户端缓存文件
对于HTTP的文件请求来说,为了保证请求的速度,会使用客户端缓存的机制。比如客户端向服务器端请求一个文件A.txt。服务器在接收到该请求之后会将A.txt文件发送给客户端。
其请求流程如下:
步骤1:客户端请求服务器端的文件
GET /file1.txt HTTP/1.1
步骤2:服务器端返回文件,并且附带额外的文件时间信息:
HTTP/1.1 200 OKDate: Mon, 23 Aug 2021 17:52:30 GMT+08:00Last-Modified: Tue, 10 Aug 2021 18:05:35 GMT+08:00Expires: Mon, 23 Aug 2021 17:53:30 GMT+08:00Cache-Control: private, max-age=60
一般来说如果客户端是现代浏览器的话,就会把A.txt缓存起来。在下次调用的时候只需要在head中添加If-Modified-Since,询问服务器该文件是否被修改了即可,如果文件没有被修改,则服务器会返回一个304 Not Modified,客户端得到该状态之后就会使用本地的缓存文件。
步骤3:客户端再次请求该文件
GET /file1.txt HTTP/1.1If-Modified-Since: Mon, 23 Aug 2021 17:55:30 GMT+08:00
步骤4:服务器端响应该请求
HTTP/1.1 304 Not ModifiedDate: Mon, 23 Aug 2021 17:55:32 GMT+08:00
在服务器的代码层面,我们首先需要返回一个响应中通常需要的日期字段,如Date、Last-Modified、Expires、Cache-Control等:
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));// 日期 headerCalendar time = new GregorianCalendar();log.info(dateFormatter.format(time.getTime()));response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time.getTime()));// 缓存 headerstime.add(Calendar.SECOND, HTTP_CACHE_SECONDS);response.headers().set(HttpHeaderNames.EXPIRES, dateFormatter.format(time.getTime()));response.headers().set(HttpHeaderNames.CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);response.headers().set(HttpHeaderNames.LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
然后在收到客户端的二次请求之后,需要比较文件的最后修改时间和If-Modified-Since中自带的时间,如果没有发送变化,则发送304状态:
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_MODIFIED, Unpooled.EMPTY_BUFFER);setDateHeader(response);
其他HTTP中常用的处理
我们讨论了文件类型和缓存,对于一个通用的HTTP服务器来说,还需要考虑很多其他常用的处理,比如异常、重定向和Keep-Alive设置。
对于异常,我们需要根据异常的代码来构造一个DefaultFullHttpResponse,并且设置相应的CONTENT_TYPE头即可,如下所示:
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, status, Unpooled.copiedBuffer("异常: " + status + "\r\n", CharsetUtil.UTF_8));response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
重定向同样需要构建一个DefaultFullHttpResponse,其状态是302 Found,并且在响应头中设置location为要跳转的URL地址即可:
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND, Unpooled.EMPTY_BUFFER);response.headers().set(HttpHeaderNames.LOCATION, newUri);
Keep-Alive是HTTP中为了避免每次请求都建立连接而做的一个优化方式。在HTTP/1.0中默认是的keep-alive是false,在HTTP/1.1中默认的keep-alive是true。如果在header中手动设置了connection:false,则server端请求返回也需要同样设置connection:false。
另外,因为HTTP/1.1中默认的keep-alive是true,如果通过HttpUtil.isKeepAlive判断通过之后,还需要判断是否是HTTP/1.0,并显示设置keep-alive为true。
final boolean keepAlive = HttpUtil.isKeepAlive(request);HttpUtil.setContentLength(response, response.content().readableBytes());if (!keepAlive) {response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);} else if (request.protocolVersion().equals(HTTP_1_0)) {response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);}
文件内容展示处理
文件内容展示处理是http服务器的核心,也是比较难以理解的地方。
首先要设置的是ContentLength,也就是响应的文件长度,这个可以使用file的length方法来获取:
RandomAccessFile raf;
raf = new RandomAccessFile(file, "r");
long fileLength = raf.length();
HttpUtil.setContentLength(response, fileLength);
然后我们需要根据文件的扩展名设置对应的CONTENT_TYPE,这个在第一小节已经介绍过了。
然后再设置date和缓存属性。这样我们就得到了一个只包含响应头的DefaultHttpResponse,我们先把这个只包含响应头的respose写到ctx中。
写完HTTP头,接下来就是写HTTP的Content了。
对于HTTP传递的文件来说,有两种处理方式,第一种方式情况下如果知道整个响应的content大小,则可以在后台直接进行整个文件的拷贝传输。如果服务器本身支持零拷贝的话,则可以使用DefaultFileRegion的transferTo方法将File或者Channel的文件进行转移。
sendFileFuture =ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise());// 结束部分lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
如果并不知道整个响应的context大小,则可以将大文件拆分成为一个个的chunk,并且在响应的头中设置transfer-coding为chunked,netty提供了HttpChunkedInput和ChunkedFile,用来将大文件拆分成为一个个的Chunk进行传输。
sendFileFuture =ctx.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)),ctx.newProgressivePromise());
如果向channel中写入ChunkedFile,则需要添加相应的ChunkedWriteHandler对chunked文件进行处理。
pipeline.addLast(new ChunkedWriteHandler());
注意,如果是完整文件传输,则需要手动添加last content部分:
lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
如果是ChunkedFile,last content部分已经包含在了chunkedFile中,不需要再手动添加了。
文件传输进度
ChannelFuture可以添加对应的listner,用来监控文件传输的进度,netty提供了一个ChannelProgressiveFutureListener,用于监控文件的进程,可以重写operationProgressed和operationComplete方法对进度监控进行定制:
sendFileFuture.addListener(new ChannelProgressiveFutureListener() {@Overridepublic void operationProgressed(ChannelProgressiveFuture future, long progress, long total) {if (total < 0) {log.info(future.channel() + " 传输进度: " + progress);} else {log.info(future.channel() + " 传输进度: " + progress + " / " + total);}}@Overridepublic void operationComplete(ChannelProgressiveFuture future) {log.info(future.channel() + " 传输完毕.");}});
总结
我们考虑了一个HTTP文件服务器最基本的一些考虑因素,现在可以使用这个文件服务器来提供服务啦!
搭建自己的下载文件服务器相关推荐
- netty系列之:搭建自己的下载文件服务器
文章目录 简介 文件的content-type 客户端缓存文件 其他HTTP中常用的处理 文件内容展示处理 文件传输进度 总结 简介 上一篇文章我们学习了如何在netty中搭建一个HTTP服务器,讨论 ...
- Python搭建简易HTTP/HTTPS文件服务器
之前因为传输文件的需要,需要自己搭建文件传输服务器.FTP/FTPS文件服务器比较容易,因为相关的软件很多.关于HTTP/HTTPS文件服务器的介绍相对来说要少很多,网上关于HTTP/HTTPS文件服 ...
- 史上最详细Docker安装最新版Minio 带详解 绝对值得收藏!!! 让我们一起学会使用minio搭建属于自己的文件服务器!!走上白嫖之路!解决启动了但是浏览器访问不了的原因
让我们一起学会使用minio搭建属于自己的文件服务器!!走上白嫖之路! WARNING: Console endpoint is listening on a dynamic port (34451) ...
- 原神私服搭建三:(启动器下载和设置代理)
原神私服搭建三:(启动器下载和设置代理) 承接:原神私服搭建二 一:GrassClipper 下载地址:https://github.com/Grasscutters/GrassClipper/rel ...
- FTP服务器搭建及自动下载文件脚本
FTP服务器搭建及自动下载文件脚本 一.FTP服务器搭建 二.浏览器与命令行测试 三.自动下载文件脚本 一.FTP服务器搭建 在虚拟机192.168.109.135上搭建FTP服务器 1.安装vsft ...
- 宝塔面板linux离线安装,宝塔linux面板搭建aria2离线下载服务器教程
宝塔linux面板搭建aria2离线下载服务器教程 刚刚搭建完就来写的,测试下载一切正常. 特别注意:美国或者其他地区的服务器会有版权限制,如果你用美国服务器下载电影时注意版权问题,否则可能会收到版权 ...
- 手把手教你搭建自己的FTP文件服务器
大家好,我是小豪,今天我来给大家分享如何在虚拟机上搭建自己的文件服务器 文章目录 Linux上安装文件服务器FTP 安装VSFTP 下载dnf 下载VSFTP 清除防火墙的iptables缓存 下载F ...
- 使用Nginx搭建前端静态服务器+文件服务器
大家可能被虚拟主机不太了解,那我们的解释一下,虚拟主机指在一台物理主机服务器上划分出多个磁盘空间,每个磁盘空间都是一个虚拟主机,每台虚拟主机都可以对外提供Web服务,并且互不干扰. 一.Nginx的虚 ...
- SSH框架总结(框架分析+环境搭建+实例源代码下载)
首先,SSH不是一个框架,而是多个框架(struts+spring+hibernate)的集成,是眼下较流行的一种Web应用程序开源集成框架,用于构建灵活.易于扩展的多层Web应用程序. 集成SSH框 ...
- EC600 QuecPython开发环境搭建、固件下载,最方便的OpenCPU物联网4G通信解决方案
EC600 QuecPython 官方资源汇总 开发环境搭建 1.安装windows驱动 2.验证模组的固件版本 3.烧录QuecPython固件 小试QuecPython 1.查看系统信息 2.点亮 ...
最新文章
- [oracle] Oracle存储过程里操作BLOB的字节数据的办法,例如写入32位整数
- C++中各种智能指针的实现及弊端(三)
- Linux监控工具介绍系列——smem
- 频繁使用花呗、借呗、微粒贷、京东白条会影响在银行的信用吗?
- C++ Error C2664:无法将参数 1 从“const char [9]”转换为“LPCWSTR”解决方案
- 如何处理Oracle中TEMP表空间满的问题(转载)
- 如何移植Android源码里面的东西到NDK
- 浅谈VMD---变分模态分解
- 东芝 rc100 linux,入门级NVMe固态硬盘首选,东芝RC100带来的超值体验
- STM32F103C8T6开发环境的搭建
- 你还记得远古时代的拨号上网么?快来了解拨号上网与宽带上网的区别
- 华为交换机console口密码配置
- java 斜率求角度_计算两条线之间的角度而不必计算斜率? (Java)
- java imageio 内存问题_java中的内存泄漏ImageIO.read()
- 苹果4s怎么越狱教程_苹果iOS 11.4-11.4.1越狱发布:附越狱教程
- C++基础之数组和字符串
- Windows10 错误代码:0xc000007b 问题解决
- Win10系统wifi图标消失无法联网怎么办
- 洛谷题目[NOIP2016 提高组]P1563 玩具谜题 详解<每日一题>
- 关于WIN10装CH340驱动的一些小小的注意事项