文件服务器
package syx;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.stream.ChunkedWriteHandler;
public class HttpFileServer {
private static final String DEFAULT_URL = "/file/";
public void run(final int port,final String url) throws Exception
{
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try
{
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception
{
socketChannel.pipeline()
.addLast("http-decoder",new HttpRequestDecoder())
.addLast("http-aggregator",new HttpObjectAggregator(65536))
.addLast("http-encoder",new HttpResponseEncoder())
.addLast("http-chunked",new ChunkedWriteHandler())
.addLast("fileServerHandler", new HttpFileServerHandler(url));
}
});
ChannelFuture future = bootstrap.bind("127.0.0.1",port).sync();
System.out.println("HTTP文件目录服务器启动,网址是:http://127.0.0.1:"+port+url);
future.channel().closeFuture().sync();
}
finally
{
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new HttpFileServer().run(8080,DEFAULT_URL);
}
}
文件处理器
package syx;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.stream.ChunkedFile;
import io.netty.util.CharsetUtil;
import javax.activation.MimetypesFileTypeMap;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.regex.Pattern;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpHeaderNames.LOCATION;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpUtil.isKeepAlive;
import static io.netty.handler.codec.http.HttpUtil.setContentLength;
import static io.netty.handler.codec.rtsp.RtspHeaderNames.CONNECTION;
import static io.netty.handler.codec.rtsp.RtspResponseStatuses.BAD_REQUEST;
import static io.netty.handler.codec.rtsp.RtspResponseStatuses.NOT_FOUND;
public class HttpFileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest>{
private final String url;
private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*");
private String sanitizeUri(String uri)
{
try
{
uri = URLDecoder.decode(uri,"UTF-8");
}
catch (UnsupportedEncodingException e)
{
throw new Error();
}
if (!uri.startsWith(url))
{
return null;
}
if (!uri.startsWith("/"))
{
return null;
}
uri = uri.replace('/', File.separatorChar);
if (uri.contains(File.separator + '.')
|| uri.contains('.'+File.separator)||uri.startsWith(".")
|| uri.endsWith(".")
|| INSECURE_URI.matcher(uri).matches())
{
return null;
}
return System.getProperty("user.dir") + File.separator + uri;
}
public HttpFileServerHandler(String url)
{
this.url = url;
}
private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status)
{
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,status,
Unpooled.copiedBuffer("Failure:"+status.toString()+"\r\n", CharsetUtil.UTF_8));
response.headers().set(CONTENT_TYPE,"text/plain;charset=UTF-8");
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
protected void channelRead0(ChannelHandlerContext channelHandlerContext, FullHttpRequest fullHttpRequest) throws Exception
{
if (!fullHttpRequest.getDecoderResult().isSuccess())
{
sendError(channelHandlerContext,BAD_REQUEST);
return ;
}
if (fullHttpRequest.getMethod() != HttpMethod.GET)
{
sendError(channelHandlerContext,METHOD_NOT_ALLOWED);
return ;
}
final String uri = fullHttpRequest.getUri();
final String path = sanitizeUri(uri);
if (path == null)
{
sendError(channelHandlerContext,FORBIDDEN);
return ;
}
File file = new File(path);
if (file.isHidden() || !file.exists())
{
sendError(channelHandlerContext,NOT_FOUND);
return;
}
if (file.isDirectory())
{
if(uri.endsWith("/"))
{
sendListing(channelHandlerContext,file);
}
else
{
sendRedirect(channelHandlerContext,uri + '/');
}
return;
}
if (!file.isFile())
{
sendError(channelHandlerContext,FORBIDDEN);
return;
}
RandomAccessFile randomAccessFile;
try
{
randomAccessFile = new RandomAccessFile(file,"r");
}catch (FileNotFoundException e)
{
sendError(channelHandlerContext,NOT_FOUND);
return;
}
long fileLength = randomAccessFile.length();
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,OK);
setContentLength(response,fileLength);
setContentTypeHeader(response,file);
if (isKeepAlive(fullHttpRequest))
{
response.headers().set(CONNECTION,HttpHeaders.Values.KEEP_ALIVE);
}
channelHandlerContext.write(response);
ChannelFuture sendFileFuture;
sendFileFuture = channelHandlerContext.write(new ChunkedFile(randomAccessFile,0,fileLength,8192),
channelHandlerContext.newProgressivePromise());
sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
public void operationProgressed(ChannelProgressiveFuture channelProgressiveFuture, long progress, long total) throws Exception
{
if (total < 0)
{
System.err.println("Transfer progress: "+progress);
}
else
{
System.err.println("Transfer progress:"+progress+"/"+total);
}
}
public void operationComplete(ChannelProgressiveFuture channelProgressiveFuture)
{
System.out.println("Transfer complete.");
}
});
ChannelFuture lastContentFuture = channelHandlerContext.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
if (!isKeepAlive(fullHttpRequest))
{
lastContentFuture.addListener(ChannelFutureListener.CLOSE);
}
}
private static void setContentTypeHeader(HttpResponse response,File file)
{
MimetypesFileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap();
response.headers().set(CONTENT_TYPE,mimetypesFileTypeMap.getContentType(file.getPath()));
}
private static final Pattern ALLOW_FILE_NAME = Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\\.]*");
private static void sendListing(ChannelHandlerContext ctx,File dir)
{
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,OK);
response.headers().set(CONTENT_TYPE,"text/html;charset=UTF-8");
StringBuilder buf = new StringBuilder();
String dirPath = dir.getPath();
buf.append("<!DOCTYPE html>\r\n");
buf.append("<html><head><title>");
buf.append(dirPath);
buf.append("目录:");
buf.append("</title></head><body>\r\n");
buf.append("<h3>");
buf.append(dirPath).append(" 目录: ");
buf.append("</h3>\r\n");
buf.append("<ul>");
buf.append("<li>链接:<a href=\"../\">..</a></li>\r\n");
for (File file : dir.listFiles())
{
if (file.isHidden() || !file.canRead())
{
continue;
}
String name = file.getName();
if (!ALLOW_FILE_NAME.matcher(name).matches())
{
continue;
}
buf.append("<li>链接:<a href=\"");
buf.append(name);
buf.append("\">");
buf.append(name);
buf.append("</a></li>\r\n");
}
buf.append("</ul></body></html>\r\n");
ByteBuf buffer = Unpooled.copiedBuffer(buf,CharsetUtil.UTF_8);
response.content().writeBytes(buffer);
buffer.release();
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
private static void sendRedirect(ChannelHandlerContext ctx,String newUri)
{
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,FOUND);
response.headers().set(LOCATION,newUri);
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}