SpringBoot集成Netty實現(xiàn)文件傳輸
點擊上方藍色字體,選擇“標星公眾號”
優(yōu)質(zhì)文章,第一時間送達
? 作者?|??47號Gamer丶
來源 |? urlify.cn/bIzUZb
實現(xiàn)瀏覽本地文件目錄,實現(xiàn)文件夾目錄的跳轉和文件的下載
添加依賴:
????org.springframework.boot
????spring-boot-starter-web
????
????????
????????????org.springframework.boot
????????????spring-boot-starter-tomcat
????????
????
????io.netty
????netty-all
????4.1.1.Final
????commons-lang
????commons-lang
????${commons.lang.version}
排除tomcat的依賴
Netty Http服務端編寫:
handler 處理類
@Component
@Slf4j
@ChannelHandler.Sharable?//@Sharable?注解用來說明ChannelHandler是否可以在多個channel直接共享使用
public?class?FileServerHandler?extends?ChannelInboundHandlerAdapter?{
???//?private?static?final?Pattern?ALLOWED_FILE_NAME?=?Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\\.]*");
????//文件存放路徑
????@Value("${netty.file.path:}")
????String?path;
????@Override
????public?void?channelRead(ChannelHandlerContext?ctx,?Object?msg)?{
????????try{
????????????if?(msg?instanceof?FullHttpRequest)?{
????????????????FullHttpRequest?req?=?(FullHttpRequest)?msg;
????????????????if(req.method()?!=?HttpMethod.GET)?{
????????????????????sendError(ctx,?HttpResponseStatus.METHOD_NOT_ALLOWED);
????????????????????return;
????????????????}
????????????????String?url?=?req.uri();
????????????????File?file?=?new?File(path?+?url);
????????????????if(file.exists()){
????????????????????if(file.isDirectory()){
????????????????????????if(url.endsWith("/"))?{
????????????????????????????sendListing(ctx,?file);
????????????????????????}else{
????????????????????????????sendRedirect(ctx,?url?+?"/");
????????????????????????}
????????????????????????return;
????????????????????}else?{
????????????????????????transferFile(?file,??ctx);
????????????????????}
????????????????}else{
????????????????????sendError(ctx,?HttpResponseStatus.NOT_FOUND);
????????????????}
????????????}
????????}catch(Exception?e){
????????????log.error("Exception:{}",e);
????????????sendError(ctx,?HttpResponseStatus.BAD_REQUEST);
????????}
????}
????/**
?????*?傳輸文件
?????*?@param?file
?????*?@param?ctx
?????*/
????private?void?transferFile(File?file,?ChannelHandlerContext?ctx){
????????try{
????????????RandomAccessFile?randomAccessFile?=?new?RandomAccessFile(file,?"r");
????????????long?fileLength?=?randomAccessFile.length();
????????????HttpResponse?response?=?new?DefaultHttpResponse(HttpVersion.HTTP_1_1,?HttpResponseStatus.OK);
????????????response.headers().set(HttpHeaderNames.CONTENT_LENGTH,?fileLength);
????????????ctx.write(response);
????????????ChannelFuture?sendFileFuture?=?ctx.write(new?ChunkedFile(randomAccessFile,?0,?fileLength,?8192),?ctx.newProgressivePromise());
????????????addListener(?sendFileFuture);
????????????ChannelFuture?lastContentFuture?=?ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
????????????lastContentFuture.addListener(ChannelFutureListener.CLOSE);
????????}catch?(Exception?e){
????????????log.error("Exception:{}",e);
????????}
????}
????/**
?????*?監(jiān)聽傳輸狀態(tài)
?????*?@param?sendFileFuture
?????*/
????private?void?addListener(?ChannelFuture?sendFileFuture){
????????sendFileFuture.addListener(new?ChannelProgressiveFutureListener()?{
????????????????@Override
????????????????public?void?operationComplete(ChannelProgressiveFuture?future)
????????????????????????throws?Exception?{
????????????????????log.debug("Transfer?complete.");
????????????????}
????????????????@Override
????????????????public?void?operationProgressed(ChannelProgressiveFuture?future,?long?progress,?long?total)?throws?Exception?{
????????????????????if(total?0){
????????????????????????log.debug("Transfer?progress:?"?+?progress);
????????????????????}else{
????????????????????????log.debug("Transfer?progress:?"?+?progress?+?"/"?+?total);
????????????????????}
????????????????}
????????});
????}
????/**
?????*?請求為目錄時,顯示文件列表
?????*?@param?ctx
?????*?@param?dir
?????*/
????private?static?void?sendListing(ChannelHandlerContext?ctx,?File?dir){
????????FullHttpResponse?response?=?new?DefaultFullHttpResponse(HttpVersion.HTTP_1_1,?HttpResponseStatus.OK);
????????response.headers().set(HttpHeaderNames.CONTENT_TYPE,?"text/html;charset=UTF-8");
????????String?dirPath?=?dir.getPath();
????????StringBuilder?buf?=?new?StringBuilder();
????????buf.append("\r\n");
????????buf.append("" );
????????buf.append(dirPath);
????????buf.append("目錄:");
????????buf.append("\r\n");
????????buf.append(""
);
????????buf.append(dirPath).append("?目錄:");
????????buf.append("\r\n");
????????buf.append(""
);
????????buf.append("鏈接: \r\n");
????????for?(File?f?:?dir.listFiles())?{
????????????if(f.isHidden()?||?!f.canRead())?{
????????????????continue;
????????????}
????????????String?name?=?f.getName();
????????????/*if?(!ALLOWED_FILE_NAME.matcher(name).matches())?{
????????????????continue;
????????????}*/
????????????buf.append("鏈接:);
????????????buf.append(name);
????????????buf.append("\">");
????????????buf.append(name);
????????????buf.append(" \r\n");
????????}
????????buf.append("\r\n");
????????ByteBuf?buffer?=?Unpooled.copiedBuffer(buf,CharsetUtil.UTF_8);
????????response.content().writeBytes(buffer);
????????buffer.release();
????????ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
????}
????/**
?????*?跳轉鏈接
?????*?@param?ctx
?????*?@param?newUri
?????*/
????private?static?void?sendRedirect(ChannelHandlerContext?ctx,?String?newUri){
????????FullHttpResponse?response?=?new?DefaultFullHttpResponse(HttpVersion.HTTP_1_1,?HttpResponseStatus.FOUND);
????????response.headers().set(HttpHeaderNames.LOCATION,?newUri);
????????ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
????}
????/**
?????*?失敗響應
?????*?@param?ctx
?????*?@param?status
?????*/
????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(HttpHeaderNames.CONTENT_TYPE,?"text/html;charset=UTF-8");
????????ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
????}
}
ChannelPipeline 實現(xiàn):
@Component
@ConditionalOnProperty(??//配置文件屬性是否為true
????????value?=?{"netty.file.enabled"},
????????matchIfMissing?=?false
)
public?class?FilePipeline?extends?ChannelInitializer?{
????@Autowired
????FileServerHandler?fleServerHandler;
????@Override
????protected?void?initChannel(SocketChannel?socketChannel)?throws?Exception?{
????????ChannelPipeline?p?=?socketChannel.pipeline();
????????p.addLast("http-decoder",?new?HttpRequestDecoder());
????????p.addLast("http-aggregator",?new?HttpObjectAggregator(65536));
????????p.addLast("http-encoder",?new?HttpResponseEncoder());
????????p.addLast("http-chunked",?new?ChunkedWriteHandler());
????????p.addLast("fileServerHandler",fleServerHandler);
????}
}
服務實現(xiàn):
@Configuration
@EnableConfigurationProperties({NettyFileProperties.class})
@ConditionalOnProperty(??//配置文件屬性是否為true
????????value?=?{"netty.file.enabled"},
????????matchIfMissing?=?false
)
@Slf4j
public?class?FileServer?{
????@Autowired
????FilePipeline?filePipeline;
????@Autowired
????NettyFileProperties?nettyFileProperties;
????@Bean("starFileServer")
????public?String?start()?{
????????Thread?thread?=??new?Thread(()?->?{
????????????NioEventLoopGroup?bossGroup?=?new?NioEventLoopGroup(nettyFileProperties.getBossThreads());
????????????NioEventLoopGroup?workerGroup?=?new?NioEventLoopGroup(nettyFileProperties.getWorkThreads());
????????????try?{
????????????????log.info("start?netty?[FileServer]?server?,port:?"?+?nettyFileProperties.getPort());
????????????????ServerBootstrap?boot?=?new?ServerBootstrap();
????????????????options(boot).group(bossGroup,?workerGroup)
????????????????????????.channel(NioServerSocketChannel.class)
????????????????????????.handler(new?LoggingHandler(LogLevel.INFO))
????????????????????????.childHandler(filePipeline);
????????????????Channel?ch?=?null;
??????????????//是否綁定IP
????????????????if(StringUtils.isNotEmpty(nettyFileProperties.getBindIp())){
????????????????????ch?=?boot.bind(nettyFileProperties.getBindIp(),nettyFileProperties.getPort()).sync().channel();
????????????????}else{
????????????????????ch?=?boot.bind(nettyFileProperties.getPort()).sync().channel();
????????????????}
????????????????ch.closeFuture().sync();
????????????}?catch?(InterruptedException?e)?{
????????????????log.error("啟動NettyServer錯誤",?e);
????????????}?finally?{
????????????????bossGroup.shutdownGracefully();
????????????????workerGroup.shutdownGracefully();
????????????}
????????});
????????thread.setName("File_Server");
????????thread.start();
????????return?"file?start";
????}
????private?ServerBootstrap?options(ServerBootstrap?boot)?{
?/*???????boot.option(ChannelOption.SO_BACKLOG,?1024)
????????????????.option(ChannelOption.TCP_NODELAY,?true)
????????????????.option(ChannelOption.ALLOCATOR,?PooledByteBufAllocator.DEFAULT);*/
????????return?boot;
????}
}
啟動配置:
---application.yml
spring.profiles.active:?file
---application-file.yml
netty:
???file:
?????enabled:?true
?????path:?d:\
?????port:?3456
測試
在瀏覽器打開http://127.0.0.1:3456/

?
粉絲福利:108本java從入門到大神精選電子書領取
???
?長按上方鋒哥微信二維碼?2 秒 備注「1234」即可獲取資料以及 可以進入java1234官方微信群
感謝點贊支持下哈?
評論
圖片
表情
