本文参考《Netty权威指南》
NettyApplication
package com.xh.netty; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class NettyApplication { public static void main(String[] args) { SpringApplication.run(NettyApplication.class, args); String[] argList =args; System.out.println("+++++++++++++Simple Netty HttpFileServer+++++++++++++++"); System.out.println("+ VERSION 1.0.1 +"); System.out.println("+ AUTHER:XH +"); System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++"); if (args.length==0){ System.out.println("Usage: java -var thisPackageName.jar [-options][args...]"); System.out.println("Use -h for more infomation"); System.out.println("default port is 8080 , webRoot is /root "); }else { for (int i=0;i<argList.length;i++){ if (argList[i].equalsIgnoreCase("-h")){ System.out.println("-p your Listern port"); System.out.println("-f your webRoot path"); System.out.println("Example:java -jar netty-0.0.1-SNAPSHOT.jar -p 80 -f /root"); return; }else { if (argList[i].equalsIgnoreCase("-p")){ try{ HttpFileServer.PORT=Integer.valueOf(argList[i+1]); }catch (NumberFormatException e){ System.out.println("wrong number for you port"); System.out.println("Use -h for more infomation"); e.printStackTrace(); return; } } if (argList[i].equalsIgnoreCase("-f")){ try{ HttpFileServer.WEBROOT=argList[i+1]; }catch (Exception e){ System.out.println("wrong path for you webRoot"); System.out.println("Use -h for more infomation"); e.printStackTrace(); return; } } } } } try { HttpFileServer.main(); } catch (InterruptedException e) { e.printStackTrace(); } } }
HttpFileServer
package com.xh.netty; 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; /** * Created by root on 8/14/17. */ public class HttpFileServer { public static String WEBROOT = "/root"; public static int PORT = 8080; public void run(final int port , final String url) throws InterruptedException { EventLoopGroup bossGroup=new NioEventLoopGroup(); EventLoopGroup workerGroup=new NioEventLoopGroup(); try{ ServerBootstrap bootstrap=new ServerBootstrap(); bootstrap.group(bossGroup,workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast("http-decoder",new HttpRequestDecoder()); socketChannel.pipeline().addLast("http-aggregator",new HttpObjectAggregator(65536)); socketChannel.pipeline().addLast("http-encoder",new HttpResponseEncoder()); socketChannel.pipeline().addLast("http-chunked",new ChunkedWriteHandler()); socketChannel.pipeline().addLast("fileServerHandler",new HttpFileServerHandler(url)); } }); ChannelFuture future = bootstrap.bind("127.0.0.1",port).sync(); System.out.println("服务器已启动>>网址:"+"127.0.0.1:"+port+url); future.channel().closeFuture().sync(); }catch (Exception e){ e.printStackTrace(); }finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main() throws InterruptedException { new HttpFileServer().run(PORT,WEBROOT); } }
HttpFileServerHandler
package com.xh.netty; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.handler.codec.http.*; import io.netty.handler.codec.http.HttpResponseStatus; 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.net.URLDecoder; import java.util.regex.Pattern; import static io.netty.handler.codec.http.HttpHeaderNames.*; import static io.netty.handler.codec.http.HttpHeaderUtil.isKeepAlive; import static io.netty.handler.codec.http.HttpHeaderUtil.setContentLength; import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE; import static io.netty.handler.codec.http.HttpResponseStatus.*; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; /** * Created by root on 8/14/17. */ public class HttpFileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest>{ private final String url; String WEBROOT = HttpFileServer.WEBROOT; public HttpFileServerHandler(String url) { this.url = url; } protected void messageReceived(ChannelHandlerContext channelHandlerContext, FullHttpRequest fullHttpRequest) throws Exception { if (!fullHttpRequest.decoderResult().isSuccess()){ sendError(channelHandlerContext, HttpResponseStatus.BAD_REQUEST); return; } if (fullHttpRequest.method()!= HttpMethod.GET){ sendError(channelHandlerContext,HttpResponseStatus.METHOD_NOT_ALLOWED); return; } String uri=fullHttpRequest.uri(); if (uri==null||uri.trim().equalsIgnoreCase("")){ uri="/"; } if (uri.trim().equalsIgnoreCase("/")){ uri= WEBROOT; } if(!uri.startsWith(WEBROOT)){ uri= WEBROOT +uri; } final String path=sanitizeUri(uri); if (path==null){ sendError(channelHandlerContext,HttpResponseStatus.FORBIDDEN); return; } File file=new File(path); if (file.isHidden()||!file.exists()){ sendError(channelHandlerContext,HttpResponseStatus.NOT_FOUND); return; } if (file.isDirectory()){ if (uri.endsWith("/")){ senfListing(channelHandlerContext,file); }else { sendRedirect(channelHandlerContext,uri+"/"); } return; } if (!file.isFile()){ sendError(channelHandlerContext,HttpResponseStatus.FORBIDDEN); return; } RandomAccessFile randomAccessFile=null; try{ randomAccessFile=new RandomAccessFile(file,"r"); }catch (FileNotFoundException e){ e.printStackTrace(); sendError(channelHandlerContext,HttpResponseStatus.NOT_FOUND); return; } Long fileLength=randomAccessFile.length(); HttpResponse httpResponse=new DefaultHttpResponse(HTTP_1_1,OK); setContentLength(httpResponse,fileLength); setContentTypeHeader(httpResponse,file); if (isKeepAlive(fullHttpRequest)){ httpResponse.headers().set(CONNECTION,KEEP_ALIVE); } channelHandlerContext.writeAndFlush(httpResponse); ChannelFuture sendFileFuture = channelHandlerContext.write( new ChunkedFile(randomAccessFile,0,fileLength,8192),channelHandlerContext.newProgressivePromise()); sendFileFuture.addListener(new ChannelProgressiveFutureListener() { public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) { if (total<0){ System.err.println("progress:"+progress); }else { System.err.println("progress:"+progress+"/"+total); } } public void operationComplete(ChannelProgressiveFuture future) { System.err.println("complete"); } }); ChannelFuture lastChannelFuture=channelHandlerContext.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); if (!isKeepAlive(fullHttpRequest)){ lastChannelFuture.addListener(ChannelFutureListener.CLOSE ); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); if (ctx.channel().isActive()){ sendError(ctx,INTERNAL_SERVER_ERROR); } } private static final Pattern INSECURE_URI=Pattern.compile(".*[<>&"].*"); public String sanitizeUri(String uri){ try{ uri= URLDecoder.decode(uri,"UTF-8"); }catch (Exception e){ try{ uri= URLDecoder.decode(uri,"ISO-8859-1"); }catch (Exception ew){ ew.printStackTrace(); } } if (!uri.startsWith(url)){ return null; } if (!uri.startsWith("/")){ return null; } uri=uri.replace('/',File.separatorChar); if (uri.contains(File.separator+'.')||uri.startsWith(".")||uri.endsWith(".")||INSECURE_URI.matcher(uri).matches()){ return null; } return uri;//System.getProperty("user.dir")+uri } private static final Pattern ALLOWED_FILE_NAME=Pattern.compile("[a-zA-Z0-9\.]*"); private void senfListing(ChannelHandlerContext channelHandlerContext, File dir) { FullHttpResponse response=new DefaultFullHttpResponse(HTTP_1_1,OK); response.headers().set(CONTENT_TYPE,"text/html;charset=UTF-8"); StringBuilder builder =new StringBuilder(); String dirPath=dir.getPath(); builder.append("<!DOCTYPE html> "); builder.append("<html><head><title>"); builder.append(dirPath); builder.append("目录:"); builder.append("</title></head><body> "); builder.append("<h3>"); builder.append(dirPath).append("目录:"); builder.append("</h3> "); builder.append("<ul>"); builder.append("<li>链接:<a href="../">..</a></li> "); for (File f:dir.listFiles()){ if (f.isHidden()||!f.canRead()){ continue; } String fname=f.getName(); if (!ALLOWED_FILE_NAME.matcher(fname).matches()){ continue; } builder.append("<li>链接:<a href=" "); builder.append(fname); builder.append("" >"); builder.append(fname); builder.append("</a></li> "); } builder.append("</ul></body></html> "); ByteBuf byteBuf= Unpooled.copiedBuffer(builder, CharsetUtil.UTF_8); response.content().writeBytes(byteBuf); byteBuf.release(); channelHandlerContext.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } private void sendRedirect(ChannelHandlerContext channelHandlerContext, String newUri) { FullHttpResponse response=new DefaultFullHttpResponse(HTTP_1_1,FOUND); response.headers().set(LOCATION,newUri); channelHandlerContext.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } private void sendError(ChannelHandlerContext channelHandlerContext, HttpResponseStatus status) { FullHttpResponse response=new DefaultFullHttpResponse( HTTP_1_1,status,Unpooled.copiedBuffer("Failure: "+status.toString()+" ", CharsetUtil.UTF_8)); response.headers().set(CONTENT_TYPE,"text/plain; charset=UTF-8"); channelHandlerContext.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } private void setContentTypeHeader(HttpResponse httpResponse, File file) { MimetypesFileTypeMap mimetypesFileTypeMap=new MimetypesFileTypeMap(); httpResponse.headers().set(CONTENT_TYPE,mimetypesFileTypeMap.getContentType(file.getPath())); } }
打包发布:
cd 到项目target同级目录
mvn clean package
然后 cd target/
java -jar netty-0.0.1-SNAPSHOT.jar -h运行