介绍

在上一篇文章中,大家学了怎样在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文件服务器的一些基本上考虑到,如今我们可以应用这一文件服务器来保证服务项目了!

评论(0条)

刀客源码 游客评论