介绍
在上一篇文章中,大家学了怎样在netty中搭建HTTP网络服务器,并探讨了如何处理和回应手机客户端推送的要求。今日,大家将探讨在netty中搭建用以文件传送的文件服务器时应当特别注意的难题。
文档的內容种类。
手机客户端向网络服务器提出一个文档,网络服务器将在回到的HTTP头里包括一个內容种类,它标示回到的文件属性。这类种类应当怎样确定?
一般来说,文件属性是依据文件后缀名来确认的。依据RFC 4288的标准,全部互联网媒体种类都务必申请注册。Apache还给予了MIME种类和文件后缀名的投射表。
由于文件属性许多,我们可以见到几类常见的种类如下所示:
MIME type后缀名image/jpegjpgimage/jpegjpegimage/pngpngtext/plaintxt text conf def list log inimage/webpwebpapplication/vnd.ms-Excelxlsapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheetxlsxapplication/msworddocapplication/vnd.openxmlformats-officedocument.wordprocessingml.documentdocxapplication/vnd.openxmlformats-officedocument.presentationml.presentationpptxapplication/vnd.ms-powerpointpptapplication/pdfpdf
JDK给予了一类MimetypesFileTypeMap,它给予了一个getContentType方式,能够依据要求的文件路径信息内容推论其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文件发给手机客户端。
要求全过程如下所示:
流程1:手机客户端要求服务端的文档 =================== GET /file1.txt HTTP/1.1 流程2:服务端回到文档,而且附加附加的文档時间信息内容: =================== HTTP/1.1 200 OK Date: Mon, 23 Aug 2021 17:52:30 GMT 08:00 Last-Modified: Tue, 10 Aug 2021 18:05:35 GMT 08:00 Expires: Mon, 23 Aug 2021 17:53:30 GMT 08:00 Cache-Control: private, max-age=60
一般来说,假如手机客户端是当代电脑浏览器,会缓存文件A.txt。在下一个启用中,您只须要在头顶部加上If-Modified-before,并了解网络服务器文档是不是已被改动。假如文档未被改动,网络服务器将回到304未修改,手机客户端将在得到此情况后应用当地缓存。
流程3:手机客户端再度要求该文件 =================== GET /file1.txt HTTP/1.1 If-Modified-Since: Mon, 23 Aug 2021 17:55:30 GMT 08:00 流程4:服务端响应当要求 =================== HTTP/1.1 304 Not Modified Date: Mon, 23 Aug 2021 17:55:32 GMT 08:00
在网络服务器的编码层,大家一方面必须回到一个回应中一般所需的日期字段名,例如日期,之前修改时间,到期時间,缓存文件操纵等。:
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US); dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE)); // 日期 header Calendar time = new GregorianCalendar(); log.info(dateFormatter.format(time.getTime())); response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time.getTime())); // 缓存文件 headers time.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())));
随后,在从手机客户端接受到第二个要求以后,必须将文档的最终修改时间与“假如改动-自”中包含的时间段开展较为,而且要是没有推送更改,则推送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,并将部位设定为URL详细地址,便于在回应头中自动跳转:
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND, Unpooled.EMPTY_BUFFER); response.headers().set(HttpHeaderNames.LOCATION, newUri);
维持主题活动是HTTP中的一种提升方法,能够防止每一个媳妇都创建联接。在HTTP/1.0中,默认设置保活为假,在HTTP/1.1中,默认设置保活为真。假如在头里手动式设定了connection: false,服务端也必须为要求回到设定connection: false。
此外,因为HTTP/1.1中初始的keep-alive为真,假如分辨是根据HttpUtil.isKeepAlive,则必须分辨是不是为HTTP/1.0,并显示设置的keep-alive为真。
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,这在第一节早已说过去了。
随后设定日期和缓存文件特性。那样,大家获得了一个只包括回应头的DefaultHttpResponse。大家最先将只包括回应头的回应载入ctx。
写完HTTP头后,下一步是写HTTP Content。
根据HTTP传送的文档有2种解决方式。第一种方式,假如了解全部回应的內容尺寸,能够立即在后台管理拷贝传送全部文档。假如网络服务器自身适用零拷贝,能够应用默认设置文档地区的transferTo方式来传输文件或安全通道的文档。
sendFileFuture = ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise()); // 完毕一部分 lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
假如您不清楚全部回应的前后文尺寸,您还可以将大文件分割成块,并将传送编号设定为回应头里的块。netty给予了HttpChunkedInput和ChunkedFile,用以将大文件拆分为块开展传送。
sendFileFuture = ctx.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)), ctx.newProgressivePromise());
假如将chunkedfile载入chunked,则必须加上对应的ChunkedWriteHandler来解决ChunkedFile。
pipeline.addLast(new ChunkedWriteHandler());
一定要注意,如果是详细的文件传送,您必须手动式加上最后一个內容一部分:
lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
如果是chunkedFile,最后一个內容一部分早已包括在ChunkedFile中,因此无需手动式加上。
文件传送进展
ChannelFuture能够加上对应的listner来监管文件传送的进展。netty给予了一个channel progressive futurelistener来监管文档的进展,而且能够遮盖operationProgressed和operationComplete方式来订制进展监管:
sendFileFuture.addListener(new ChannelProgressiveFutureListener() { @Override public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) { if (total < 0) { log.info(future.channel() " 传送进展: " progress); } else { log.info(future.channel() " 传送进展: " progress " / " total); } } @Override public void operationComplete(ChannelProgressiveFuture future) { log.info(future.channel() " 传送结束."); } });
引言
大家开始考虑到了HTTP文件服务器的一些基本上考虑到,如今我们可以应用这一文件服务器来保证服务项目了!
1.本站大部分内容均收集于网络!若内容若侵犯到您的权益,请发送邮件至:duhaomu@163.com,我们将第一时间处理!
2.资源所需价格并非资源售卖价格,是收集、整理、编辑详情以及本站运营的适当补贴,并且本站不提供任何免费技术支持。
3.所有资源仅限于参考和学习,版权归原作者所有,更多请阅读网站声明。