package bhz.netty.upload; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.util.SelfSignedCertificate; /** * A HTTP server showing how to use the HTTP multipart package for file uploads and decoding post data. */ public final class HttpUploadServer { static final boolean SSL = System.getProperty("ssl") != null; static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080")); public static void main(String[] args) throws Exception { // Configure SSL. final SslContext sslCtx; if (SSL) { SelfSignedCertificate ssc = new SelfSignedCertificate(); sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey()); } else { sslCtx = null; } EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup); b.channel(NioServerSocketChannel.class); b.handler(new LoggingHandler(LogLevel.INFO)); b.childHandler(new HttpUploadServerInitializer(sslCtx)); Channel ch = b.bind(PORT).sync().channel(); System.err.println("Open your web browser and navigate to " + (SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/'); ch.closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
package bhz.netty.upload; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.Cookie; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderUtil; import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.ServerCookieDecoder; import io.netty.handler.codec.http.ServerCookieEncoder; import io.netty.handler.codec.http.multipart.Attribute; import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory; import io.netty.handler.codec.http.multipart.DiskAttribute; import io.netty.handler.codec.http.multipart.DiskFileUpload; import io.netty.handler.codec.http.multipart.FileUpload; import io.netty.handler.codec.http.multipart.HttpDataFactory; import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder; import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException; import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; import io.netty.handler.codec.http.multipart.InterfaceHttpData; import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType; import io.netty.util.CharsetUtil; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.URI; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import static io.netty.buffer.Unpooled.*; public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObject> { private static final Logger logger = Logger.getLogger(HttpUploadServerHandler.class.getName()); private HttpRequest request; private boolean readingChunks; private final StringBuilder responseContent = new StringBuilder(); private static final HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); // Disk if size exceed private HttpPostRequestDecoder decoder; static { DiskFileUpload.deleteOnExitTemporaryFile = true; // should delete file // on exit (in normal // exit) DiskFileUpload.baseDirectory = "D:" + File.separatorChar + "aa"; // system temp directory DiskAttribute.deleteOnExitTemporaryFile = true; // should delete file on // exit (in normal exit) DiskAttribute.baseDirectory = "D:" + File.separatorChar + "aa"; // system temp directory } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { if (decoder != null) { decoder.cleanFiles(); } } @Override public void messageReceived(ChannelHandlerContext ctx, HttpObject msg) throws Exception { if (msg instanceof HttpRequest) { HttpRequest request = this.request = (HttpRequest) msg; URI uri = new URI(request.uri()); if (!uri.getPath().startsWith("/form")) { // Write Menu writeMenu(ctx); return; } responseContent.setLength(0); responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n"); responseContent.append("===================================\r\n"); responseContent.append("VERSION: " + request.protocolVersion().text() + "\r\n"); responseContent.append("REQUEST_URI: " + request.uri() + "\r\n\r\n"); responseContent.append("\r\n\r\n"); // new getMethod for (Entry<CharSequence, CharSequence> entry : request.headers()) { responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n"); } responseContent.append("\r\n\r\n"); // new getMethod Set<Cookie> cookies; String value = request.headers().getAndConvert(HttpHeaderNames.COOKIE); if (value == null) { cookies = Collections.emptySet(); } else { cookies = ServerCookieDecoder.decode(value); } for (Cookie cookie : cookies) { responseContent.append("COOKIE: " + cookie + "\r\n"); } responseContent.append("\r\n\r\n"); QueryStringDecoder decoderQuery = new QueryStringDecoder(request.uri()); Map<String, List<String>> uriAttributes = decoderQuery.parameters(); for (Entry<String, List<String>> attr: uriAttributes.entrySet()) { for (String attrVal: attr.getValue()) { responseContent.append("URI: " + attr.getKey() + '=' + attrVal + "\r\n"); } } responseContent.append("\r\n\r\n"); // if GET Method: should not try to create a HttpPostRequestDecoder if (request.method().equals(HttpMethod.GET)) { // GET Method: should not try to create a HttpPostRequestDecoder // So stop here responseContent.append("\r\n\r\nEND OF GET CONTENT\r\n"); // Not now: LastHttpContent will be sent writeResponse(ctx.channel()); return; } try { decoder = new HttpPostRequestDecoder(factory, request); } catch (ErrorDataDecoderException e1) { e1.printStackTrace(); responseContent.append(e1.getMessage()); writeResponse(ctx.channel()); ctx.channel().close(); return; } readingChunks = HttpHeaderUtil.isTransferEncodingChunked(request); responseContent.append("Is Chunked: " + readingChunks + "\r\n"); responseContent.append("IsMultipart: " + decoder.isMultipart() + "\r\n"); if (readingChunks) { // Chunk version responseContent.append("Chunks: "); readingChunks = true; } } // check if the decoder was constructed before // if not it handles the form get if (decoder != null) { if (msg instanceof HttpContent) { // New chunk is received HttpContent chunk = (HttpContent) msg; try { decoder.offer(chunk); } catch (ErrorDataDecoderException e1) { e1.printStackTrace(); responseContent.append(e1.getMessage()); writeResponse(ctx.channel()); ctx.channel().close(); return; } responseContent.append('o'); // example of reading chunk by chunk (minimize memory usage due to // Factory) readHttpDataChunkByChunk(); // example of reading only if at the end if (chunk instanceof LastHttpContent) { writeResponse(ctx.channel()); readingChunks = false; reset(); } } } else { writeResponse(ctx.channel()); } } private void reset() { request = null; // destroy the decoder to release all resources decoder.destroy(); decoder = null; } /** * Example of reading request by chunk and getting values from chunk to chunk */ private void readHttpDataChunkByChunk() throws Exception { try { while (decoder.hasNext()) { InterfaceHttpData data = decoder.next(); if (data != null) { try { // new value writeHttpData(data); } finally { data.release(); } } } } catch (EndOfDataDecoderException e1) { // end responseContent.append("\r\n\r\nEND OF CONTENT CHUNK BY CHUNK\r\n\r\n"); } } private void writeHttpData(InterfaceHttpData data) throws Exception { if (data.getHttpDataType() == HttpDataType.Attribute) { Attribute attribute = (Attribute) data; String value; try { value = attribute.getValue(); } catch (IOException e1) { // Error while reading data from File, only print name and error e1.printStackTrace(); responseContent.append("\r\nBODY Attribute: " + attribute.getHttpDataType().name() + ": " + attribute.getName() + " Error while reading value: " + e1.getMessage() + "\r\n"); return; } if (value.length() > 100) { responseContent.append("\r\nBODY Attribute: " + attribute.getHttpDataType().name() + ": " + attribute.getName() + " data too long\r\n"); } else { responseContent.append("\r\nBODY Attribute: " + attribute.getHttpDataType().name() + ": " + attribute + "\r\n"); } } else { responseContent.append("\r\n -----------start-------------" + "\r\n"); responseContent.append("\r\nBODY FileUpload: " + data.getHttpDataType().name() + ": " + data + "\r\n"); responseContent.append("\r\n ------------end------------" + "\r\n"); if (data.getHttpDataType() == HttpDataType.FileUpload) { FileUpload fileUpload = (FileUpload) data; if (fileUpload.isCompleted()) { System.out.println("file name : " + fileUpload.getFilename()); System.out.println("file length: " + fileUpload.length()); System.out.println("file maxSize : " + fileUpload.getMaxSize()); System.out.println("file path :" + fileUpload.getFile().getPath()); System.out.println("file absolutepath :" + fileUpload.getFile().getAbsolutePath()); System.out.println("parent path :" + fileUpload.getFile().getParentFile()); if (fileUpload.length() < 1024*1024*10) { responseContent.append("\tContent of file\r\n"); try { //responseContent.append(fileUpload.getString(fileUpload.getCharset())); } catch (Exception e1) { // do nothing for the example e1.printStackTrace(); } responseContent.append("\r\n"); } else { responseContent.append("\tFile too long to be printed out:" + fileUpload.length() + "\r\n"); } // fileUpload.isInMemory();// tells if the file is in Memory // or on File fileUpload.renameTo(new File(fileUpload.getFile().getPath())); // enable to move into another // File dest //decoder.removeFileUploadFromClean(fileUpload); //remove // the File of to delete file } else { responseContent.append("\tFile to be continued but should not!\r\n"); } } } } private void writeResponse(Channel channel) { // Convert the response content to a ChannelBuffer. ByteBuf buf = copiedBuffer(responseContent.toString(), CharsetUtil.UTF_8); responseContent.setLength(0); // Decide whether to close the connection or not. boolean close = request.headers().contains(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE, true) || request.protocolVersion().equals(HttpVersion.HTTP_1_0) && !request.headers().contains(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE, true); // Build the response object. FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf); response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8"); if (!close) { // There's no need to add 'Content-Length' header // if this is the last response. response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, buf.readableBytes()); } Set<Cookie> cookies; String value = request.headers().getAndConvert(HttpHeaderNames.COOKIE); if (value == null) { cookies = Collections.emptySet(); } else { cookies = ServerCookieDecoder.decode(value); } if (!cookies.isEmpty()) { // Reset the cookies if necessary. for (Cookie cookie : cookies) { response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.encode(cookie)); } } // Write the response. ChannelFuture future = channel.writeAndFlush(response); // Close the connection after the write operation is done if necessary. if (close) { future.addListener(ChannelFutureListener.CLOSE); } } private void writeMenu(ChannelHandlerContext ctx) { // print several HTML forms // Convert the response content to a ChannelBuffer. responseContent.setLength(0); // create Pseudo Menu responseContent.append("<html>"); responseContent.append("<head>"); responseContent.append("<title>Netty Test Form</title>\r\n"); responseContent.append("</head>\r\n"); responseContent.append("<body bgcolor=white><style>td{font-size: 12pt;}</style>"); responseContent.append("<table border=\"0\">"); responseContent.append("<tr>"); responseContent.append("<td>"); responseContent.append("<h1>Netty Test Form</h1>"); responseContent.append("Choose one FORM"); responseContent.append("</td>"); responseContent.append("</tr>"); responseContent.append("</table>\r\n"); // GET responseContent.append("<CENTER>GET FORM<HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>"); responseContent.append("<FORM ACTION=\"/formget\" METHOD=\"GET\">"); responseContent.append("<input type=hidden name=getform value=\"GET\">"); responseContent.append("<table border=\"0\">"); responseContent.append("<tr><td>Fill with value: <br> <input type=text name=\"info\" size=10></td></tr>"); responseContent.append("<tr><td>Fill with value: <br> <input type=text name=\"secondinfo\" size=20>"); responseContent .append("<tr><td>Fill with value: <br> <textarea name=\"thirdinfo\" cols=40 rows=10></textarea>"); responseContent.append("</td></tr>"); responseContent.append("<tr><td><INPUT TYPE=\"submit\" NAME=\"Send\" VALUE=\"Send\"></INPUT></td>"); responseContent.append("<td><INPUT TYPE=\"reset\" NAME=\"Clear\" VALUE=\"Clear\" ></INPUT></td></tr>"); responseContent.append("</table></FORM>\r\n"); responseContent.append("<CENTER><HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>"); // POST responseContent.append("<CENTER>POST FORM<HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>"); responseContent.append("<FORM ACTION=\"/formpost\" METHOD=\"POST\">"); responseContent.append("<input type=hidden name=getform value=\"POST\">"); responseContent.append("<table border=\"0\">"); responseContent.append("<tr><td>Fill with value: <br> <input type=text name=\"info\" size=10></td></tr>"); responseContent.append("<tr><td>Fill with value: <br> <input type=text name=\"secondinfo\" size=20>"); responseContent .append("<tr><td>Fill with value: <br> <textarea name=\"thirdinfo\" cols=40 rows=10></textarea>"); responseContent.append("<tr><td>Fill with file (only file name will be transmitted): <br> " + "<input type=file name=\"myfile\">"); responseContent.append("</td></tr>"); responseContent.append("<tr><td><INPUT TYPE=\"submit\" NAME=\"Send\" VALUE=\"Send\"></INPUT></td>"); responseContent.append("<td><INPUT TYPE=\"reset\" NAME=\"Clear\" VALUE=\"Clear\" ></INPUT></td></tr>"); responseContent.append("</table></FORM>\r\n"); responseContent.append("<CENTER><HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>"); // POST with enctype="multipart/form-data" responseContent.append("<CENTER>POST MULTIPART FORM<HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>"); responseContent.append("<FORM ACTION=\"/formpostmultipart\" ENCTYPE=\"multipart/form-data\" METHOD=\"POST\">"); responseContent.append("<input type=hidden name=getform value=\"POST\">"); responseContent.append("<table border=\"0\">"); responseContent.append("<tr><td>Fill with value: <br> <input type=text name=\"info\" size=10></td></tr>"); responseContent.append("<tr><td>Fill with value: <br> <input type=text name=\"secondinfo\" size=20>"); responseContent .append("<tr><td>Fill with value: <br> <textarea name=\"thirdinfo\" cols=40 rows=10></textarea>"); responseContent.append("<tr><td>Fill with file: <br> <input type=file name=\"myfile\">"); responseContent.append("</td></tr>"); responseContent.append("<tr><td><INPUT TYPE=\"submit\" NAME=\"Send\" VALUE=\"Send\"></INPUT></td>"); responseContent.append("<td><INPUT TYPE=\"reset\" NAME=\"Clear\" VALUE=\"Clear\" ></INPUT></td></tr>"); responseContent.append("</table></FORM>\r\n"); responseContent.append("<CENTER><HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>"); responseContent.append("</body>"); responseContent.append("</html>"); ByteBuf buf = copiedBuffer(responseContent.toString(), CharsetUtil.UTF_8); // Build the response object. FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf); response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8"); response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, buf.readableBytes()); // Write the response. ctx.channel().writeAndFlush(response); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.log(Level.WARNING, responseContent.toString(), cause); ctx.channel().close(); } }
package bhz.netty.upload; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpContentCompressor; import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.ssl.SslContext; public class HttpUploadServerInitializer extends ChannelInitializer<SocketChannel> { private final SslContext sslCtx; public HttpUploadServerInitializer(SslContext sslCtx) { this.sslCtx = sslCtx; } @Override public void initChannel(SocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); if (sslCtx != null) { pipeline.addLast(sslCtx.newHandler(ch.alloc())); } pipeline.addLast(new HttpRequestDecoder()); pipeline.addLast(new HttpResponseEncoder()); // Remove the following line if you don't want automatic content compression. pipeline.addLast(new HttpContentCompressor()); pipeline.addLast(new HttpUploadServerHandler()); } }
相关推荐
- **权限控制**:服务器端需要对上传的文件路径进行安全控制,避免文件覆盖或访问敏感目录。 总结,利用 Netty 实现文件传输是一个涉及网络编程、多线程、I/O 操作以及内存管理的综合实践。通过 Netty 的强大功能...
8. **ChannelFuture**: ChannelFuture代表了一个通道的操作结果,可以通过监听它来获取操作完成的通知,这对于异步操作如大文件上传非常有用。 在实际应用中,我们需要创建一个ServerBootstrap实例,配置...
在处理大文件上传时,由于单次传输可能会超过网络缓冲区的限制,因此Netty提供了分片上传文件的功能。这个小例子就是展示了如何在Netty4中实现文件的分片上传。 首先,我们来理解"分片"的概念。在文件传输中,分片...
1. 使用合适的HttpDecoder和HttpEncoder,如ChunkedInput和ChunkedOutput,处理大文件上传或流式传输。 2. 自定义RequestHandler,实现更高效的数据解析,如通过ByteBuf提取请求头和请求体,避免不必要的对象创建。 ...
在构建一个基于Netty的文件上传系统时,我们首先需要理解Netty的核心特性和Spring Boot在其中的作用。Netty是一个高性能、异步事件驱动的网络应用程序框架,它简化了网络应用的开发,尤其是在处理TCP、UDP协议时。而...
在这个项目"Netty-fileServer"中,它被用来构建一个简单的文件服务器,实现了基于网络的文件上传和下载功能。让我们深入探讨这个项目所涉及的Netty核心概念和技术。 1. **Netty Channel**: 在Netty中,Channel是...
在服务端,当接收到客户端的文件上传请求时,会创建一个`FileChannel`来读取文件内容,并通过`channel.writeAndFlush(fileRegion)`将其写入网络。在客户端,通过监听`WriteCompletionEvent`来确认文件是否已完全发送...
通常会有一个专门的FileTransferHandler来处理文件上传和下载的逻辑。 6. **表情发送** 表情发送通常需要一个表情编码和解码的过程。可以创建一个自定义的编码器,将表情文本转换成对应的编码,如表情图片的URL或...
在描述中提到的“文件上传下载功能”是现代聊天室应用的一个重要特性。这个功能允许用户将文件发送给其他在线用户,或者从服务器上下载他人分享的文件。在Java实现中,这通常涉及到多线程和网络编程技术,因为文件...
本书还涵盖了Netty在实际项目中的应用,例如,如何构建WebSocket服务器、如何实现HTTP/2服务器、以及如何处理大型文件上传和下载等。这些实例将帮助读者理解Netty在实际工作中的应用场景,并提升解决问题的能力。 ...
基于Netty4的文件上传服务,支持断点续传,maven项目实例源代码。 客户端: 使用JDK操作文件指针,可分段读取、分段写入的特性,客户端每次发送文件的一部分,并累计发送的文件内容的起始点,把内容、文件名称、...
总的来说,“niubanUploadNetty.zip”提供的实例展示了如何结合ZooKeeper的分布式协调能力和Netty的高性能网络通信能力,实现一个简单而实用的图片上传系统。这个例子不仅提供了学习分布式系统和网络编程的实践素材...
在Java中,文件上传通常使用各种框架(如Apache Commons FileUpload、Spring的MultipartFile等)或者通过Socket编程实现自定义的上传协议。 5. 阻塞式IO流使用 阻塞式IO流是Java中实现文件操作的传统方式。以...
Java文件上传组件 COS FAT文件系统读写类库 fat32-lib fat32-lib 是一个用来读写 FAT 16/32 格式文件系统的纯 Java 类库(纯的)。 Eclipse的HTML格式化插件 Eclipse Tidy Eclipse HTML Tidy 是一款 Eclipse 的...
本文通过Spring Boot结合LayIM和t-io实现了一个文件上传以及监听用户状态的功能,并提供了一个实例代码供参考。 ### 实现文件上传的关键知识点 1. **Spring Boot 文件上传处理**: Spring Boot在处理文件上传方面...
`NettyFileUpload`继承自`FileUpload`,并且包含了一个`NettyRequestContext`实例,这表明该类是专门为处理Netty框架中的文件上传而设计的。其中,`isMultipartContent`方法用于检查请求是否为多部分内容类型,这是...
Java二进制IO类与文件复制操作实例 16个目标文件 内容索引:Java源码,初学实例,二进制,文件复制 Java二进制IO类与文件复制操作实例,好像是一本书的例子,源代码有的是独立运行的,与同目录下的其它代码文件互不联系...
例如,我们可以使用它来下载网页内容,或者上传文件到服务器。 总结起来,Java网络编程实例涵盖了从底层的Socket通信到高层的J2EE框架如Servlet和Struts的使用,这些都是构建基于Java的网络应用程序的基础。通过...
- JSch提供`Sftp`子类,通过`ChannelSftp`可以实现文件的上传和下载。`ChannelSftp`提供了丰富的文件操作API,如`put()`, `get()`, `cd()`, `ls()`等。 - 文件传输通常涉及到权限、路径和文件的读写操作,需要特别...
通过这些接口,开发者可以实现文件的上传和下载,以及邮件的发送和接收。 书中的源码部分是理解这些概念的关键,它们展示了如何将理论知识转化为实际操作。例如,读者可以通过阅读源码学习如何构建一个简单的HTTP...