从linux上拿文件到本地 服务器下载文件到本地( 二 )

其他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());【从linux上拿文件到本地 服务器下载文件到本地】注意,如果是完整文件传输,则需要手动添加last content部分:
lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);如果是ChunkedFile,last content部分已经包含在了chunkedFile中,不需要再手动添加了 。

推荐阅读