Author: rickiyang reference: www.cnblogs.com/rickiyang/p…

Today we are going to complete a task using Netty for file transfer. In real projects, file transfer is usually done through FTP or HTTP attachments. In fact, TCP Socket+File has some application scenarios, although it is not the mainstream, but it is important to master this File transfer method, especially for persistent data exchange between two JVM processes across the host.

Using Netty for file transfer also takes advantage of Netty’s natural advantage: zero-copy capability. Netty’s “zero copy” feature is one of the most common features of netty’s “zero copy” feature.

Netty’s “Zero copy” is mainly reflected in the following three aspects:

  1. Netty receives and sends bytebuffers using DIRECT BUFFERS, which use out-of-heap DIRECT memory for Socket reading and writing without the need for secondary copy of byte BUFFERS. If traditional HEAP BUFFERS are used for Socket reads and writes, the JVM copies the HEAP Buffer into direct memory before writing it to the Socket. The message is sent with an extra memory copy of the buffer compared to direct out-of-heap memory.

  2. Netty provides the combined Buffer object, which can aggregate multiple ByteBuffer objects. Users can operate the combined Buffer as conveniently as one Buffer, avoiding the traditional way of combining several small buffers into one large Buffer through memory copy.

  3. Netty adopts the transferTo method to transfer files, which can directly send the data in the file buffer to the target Channel, avoiding the memory copy problem caused by the traditional write method.

Specific analysis is not introduced here, interested can refer to the relevant documents. Let’s focus on file transfer. Netty as a high-performance server-side asynchronous IO framework must also be indispensable to the file read and write function, we can use Netty simulation HTTP form through the web page to upload files to the server, of course, to use HTTP form that you do not need Netty! You are overqualified for a job.

In Netty4, if you want to upload files using HTTP, you also have to use a third-party JAR package: okhttp. Use this JAR to send the HTTP request. But in Netty5 has been written for us, we can directly call the NEtty5 API can be implemented. So the difference between Netty4 and 5 is still quite big, as for the use of which, that depends on your company to choose which one! This article uses Netty4 to realize file upload function. Here’s the code:

Pom file:

<dependency> <groupId> io.ty </groupId> <artifactId>netty-all</artifactId> <version>4.1.5.Final</version> </dependency>Copy the code

Server side:

import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectDecoder; import io.netty.handler.codec.serialization.ObjectEncoder; public class FileUploadServer { public void bind(int port) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024).childHandler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) throws Exception  { ch.pipeline().addLast(new ObjectEncoder()); ch.pipeline().addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.weakCachingConcurrentResolver(null))); Ch.pipeline ().addLast(new FileUploadServerHandler()); }}); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) { int port = 8080; if (args ! = null && args.length > 0) { try { port = Integer.valueOf(args[0]); } catch (NumberFormatException e) { e.printStackTrace(); } } try { new FileUploadServer().bind(port); } catch (Exception e) { e.printStackTrace(); }}}Copy the code

The server end handler:

import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.io.File; import java.io.RandomAccessFile; public class FileUploadServerHandler extends ChannelInboundHandlerAdapter { private int byteRead; private volatile int start = 0; private String file_dir = "D:"; @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof FileUploadFile) { FileUploadFile ef = (FileUploadFile) msg; byte[] bytes = ef.getBytes(); byteRead = ef.getEndPos(); String md5 = ef.getFile_md5(); // File name String path = file_dir + file. separator + md5; File file = new File(path); RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); randomAccessFile.seek(start); randomAccessFile.write(bytes); start = start + byteRead; if (byteRead > 0) { ctx.writeAndFlush(start); } else { randomAccessFile.close(); ctx.close(); } } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); }}Copy the code

The client side:

import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectDecoder; import io.netty.handler.codec.serialization.ObjectEncoder; import java.io.File; public class FileUploadClient { public void connect(int port, String host, final FileUploadFile fileUploadFile) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast(new ObjectEncoder()); ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.weakCachingConcurrentResolver(null))); ch.pipeline().addLast(new FileUploadClientHandler(fileUploadFile)); }}); ChannelFuture f = b.connect(host, port).sync(); f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } public static void main(String[] args) { int port = 8080; if (args ! = null && args.length > 0) { try { port = Integer.valueOf(args[0]); } catch (NumberFormatException e) { e.printStackTrace(); } } try { FileUploadFile uploadFile = new FileUploadFile(); File file = new File("c:/1.txt"); String fileMd5 = file.getName(); // file name uploadfile.setfile (file); uploadFile.setFile_md5(fileMd5); uploadFile.setStarPos(0); New FileUploadClient().connect(port, "127.0.0.1", uploadFile); } catch (Exception e) { e.printStackTrace(); }}}Copy the code

The client end handler:

import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; public class FileUploadClientHandler extends ChannelInboundHandlerAdapter { private int byteRead; private volatile int start = 0; private volatile int lastLength = 0; public RandomAccessFile randomAccessFile; private FileUploadFile fileUploadFile; public FileUploadClientHandler(FileUploadFile ef) { if (ef.getFile().exists()) { if (! ef.getFile().isFile()) { System.out.println("Not a file :" + ef.getFile()); return; } } this.fileUploadFile = ef; } public void channelActive(ChannelHandlerContext ctx) { try { randomAccessFile = new RandomAccessFile(fileUploadFile.getFile(), "r"); randomAccessFile.seek(fileUploadFile.getStarPos()); lastLength = (int) randomAccessFile.length() / 10; byte[] bytes = new byte[lastLength]; if ((byteRead = randomAccessFile.read(bytes)) ! = -1) { fileUploadFile.setEndPos(byteRead); fileUploadFile.setBytes(bytes); ctx.writeAndFlush(fileUploadFile); } else {system.out.println (" file has been read "); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException i) { i.printStackTrace(); } } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof Integer) { start = (Integer) msg; if (start ! = -1) { randomAccessFile = new RandomAccessFile(fileUploadFile.getFile(), "r"); randomAccessFile.seek(start); System.out.println(" block length: "+ (randomAccessFile.length() / 10)); System.out.println(" length: "+ (randomAccessFile.length() -start)); int a = (int) (randomAccessFile.length() - start); int b = (int) (randomAccessFile.length() / 10); if (a < b) { lastLength = a; } byte[] bytes = new byte[lastLength]; System.out.println("-----------------------------" + bytes.length); if ((byteRead = randomAccessFile.read(bytes)) ! = 1 && (randomAccessFile.length() -start) > 0) {system.out.println ("byte length: "+ bytes.length); fileUploadFile.setEndPos(byteRead); fileUploadFile.setBytes(bytes); try { ctx.writeAndFlush(fileUploadFile); } catch (Exception e) { e.printStackTrace(); } } else { randomAccessFile.close(); ctx.close(); System.out.println(" file read --------" + byteRead); } } } } // @Override // public void channelRead(ChannelHandlerContext ctx, Object MSG) throws // Exception {// system.out.println ("Server is speek: "+msg.toString()); // FileRegion filer = (FileRegion) msg; // String path = "E://Apk//APKMD5.txt"; // File fl = new File(path); // fl.createNewFile(); // RandomAccessFile rdafile = new RandomAccessFile(path, "rw"); // FileRegion f = new DefaultFileRegion(rdafile.getChannel(), 0, // rdafile.length()); // // System.out.println("This is" + ++counter + "times receive server:[" // + msg + "]"); // } // @Override // public void channelReadComplete(ChannelHandlerContext ctx) throws // Exception { // ctx.flush(); // } public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } // @Override // protected void channelRead0(ChannelHandlerContext ctx, String msg) // throws Exception { // String a = msg; // System.out.println("This is"+ // ++counter+"times receive server:["+msg+"]"); / /}}Copy the code

We also created a custom object to count file upload progress:

import java.io.File; import java.io.Serializable; public class FileUploadFile implements Serializable { private static final long serialVersionUID = 1L; private File file; // File private String file_md5; // file name private int starPos; // Start position private byte[] bytes; Private int endPos; Public int getStarPos() {return starPos; } public void setStarPos(int starPos) { this.starPos = starPos; } public int getEndPos() { return endPos; } public void setEndPos(int endPos) { this.endPos = endPos; } public byte[] getBytes() { return bytes; } public void setBytes(byte[] bytes) { this.bytes = bytes; } public File getFile() { return file; } public void setFile(File file) { this.file = file; } public String getFile_md5() { return file_md5; } public void setFile_md5(String file_md5) { this.file_md5 = file_md5; }}Copy the code

The output is:

Rocks length: 894 length: 8052 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 894 byte length: 894 block of length: 894 length: 7158 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 894 byte length: Length: 894 block of length: 894, 6264 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 894 - byte length: 894 block of length: 894 length: 5370 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 894 byte length: 894 block of length: 894 length: 4476 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 894 byte length: 894 block of length: Length: 894 to 3582 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 894 byte length: 894 block of length: length: 894, 2688 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 894 - byte length: Length: 894 block of length: 894, 1794 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 894 - byte length: 894 block of length: 894 length: 900 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 894 byte length: 894 block of length: 894 length: 6 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 6 byte length: 6 block length: 894 length: 0 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 0 file has finished reading -- -- -- -- -- -- -- -- 0 Process finished with exit code 0Copy the code

This allows you to upload files on the server side, or you can use HTTP.

Server side:

import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; public class HttpFileServer implements Runnable { private int port; public HttpFileServer(int port) { super(); this.port = port; } @Override public void run() { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup); serverBootstrap.channel(NioServerSocketChannel.class); //serverBootstrap.handler(new LoggingHandler(LogLevel.INFO)); serverBootstrap.childHandler(new HttpChannelInitlalizer()); try { ChannelFuture f = serverBootstrap.bind(port).sync(); f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) { HttpFileServer b = new HttpFileServer(9003); new Thread(b).start(); }}Copy the code

The Server end, initializer:

import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.stream.ChunkedWriteHandler; public class HttpChannelInitlalizer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpObjectAggregator(65536)); pipeline.addLast(new ChunkedWriteHandler()); pipeline.addLast(new HttpChannelHandler()); }}Copy the code

The server end hadler:

import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelProgressiveFuture;
import io.netty.channel.ChannelProgressiveFutureListener;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpChunkedInput;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponse;
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.stream.ChunkedFile;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.SystemPropertyUtil;

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 javax.activation.MimetypesFileTypeMap;

public class HttpChannelHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
    public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
    public static final String HTTP_DATE_GMT_TIMEZONE = "GMT";
    public static final int HTTP_CACHE_SECONDS = 60;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        // 监测解码情况
        if (!request.getDecoderResult().isSuccess()) {
            sendError(ctx, BAD_REQUEST);
            return;
        }
        final String uri = request.getUri();
        final String path = sanitizeUri(uri);
        System.out.println("get file:"+path);
        if (path == null) {
            sendError(ctx, FORBIDDEN);
            return;
        }
        //读取要下载的文件
        File file = new File(path);
        if (file.isHidden() || !file.exists()) {
            sendError(ctx, NOT_FOUND);
            return;
        }
        if (!file.isFile()) {
            sendError(ctx, FORBIDDEN);
            return;
        }
        RandomAccessFile raf;
        try {
            raf = new RandomAccessFile(file, "r");
        } catch (FileNotFoundException ignore) {
            sendError(ctx, NOT_FOUND);
            return;
        }
        long fileLength = raf.length();
        HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        HttpHeaders.setContentLength(response, fileLength);
        setContentTypeHeader(response, file);
        //setDateAndCacheHeaders(response, file);
        if (HttpHeaders.isKeepAlive(request)) {
            response.headers().set("CONNECTION", HttpHeaders.Values.KEEP_ALIVE);
        }

        // Write the initial line and the header.
        ctx.write(response);

        // Write the content.
        ChannelFuture sendFileFuture =
        ctx.write(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)), ctx.newProgressivePromise());
        //sendFuture用于监视发送数据的状态
        sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
            @Override
            public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) {
                if (total < 0) { // total unknown
                    System.err.println(future.channel() + " Transfer progress: " + progress);
                } else {
                    System.err.println(future.channel() + " Transfer progress: " + progress + " / " + total);
                }
            }

            @Override
            public void operationComplete(ChannelProgressiveFuture future) {
                System.err.println(future.channel() + " Transfer complete.");
            }
        });

        // Write the end marker
        ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);

        // Decide whether to close the connection or not.
        if (!HttpHeaders.isKeepAlive(request)) {
            // Close the connection when the whole content is written out.
            lastContentFuture.addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        if (ctx.channel().isActive()) {
            sendError(ctx, INTERNAL_SERVER_ERROR);
        }
        ctx.close();
    }

    private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*");

    private static String sanitizeUri(String uri) {
        // Decode the path.
        try {
            uri = URLDecoder.decode(uri, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new Error(e);
        }

        if (!uri.startsWith("/")) {
            return null;
        }

        // Convert file separators.
        uri = uri.replace('/', File.separatorChar);

        // Simplistic dumb security check.
        // You will have to do something serious in the production environment.
        if (uri.contains(File.separator + '.') || uri.contains('.' + File.separator) || uri.startsWith(".") || uri.endsWith(".")
                || INSECURE_URI.matcher(uri).matches()) {
            return null;
        }

        // Convert to absolute path.
        return SystemPropertyUtil.get("user.dir") + File.separator + uri;
    }


    private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8));
        response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");

        // Close the connection as soon as the error message is sent.
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }

    /**
     * Sets the content type header for the HTTP Response
     *
     * @param response
     *            HTTP response
     * @param file
     *            file to extract content type
     */
    private static void setContentTypeHeader(HttpResponse response, File file) {
        MimetypesFileTypeMap m = new MimetypesFileTypeMap();
        String contentType = m.getContentType(file.getPath());
        if (!contentType.equals("application/octet-stream")) {
            contentType += "; charset=utf-8";
        }
        response.headers().set(CONTENT_TYPE, contentType);
    }

}
Copy the code

The client side:

import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequestEncoder; import io.netty.handler.codec.http.HttpResponseDecoder; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.stream.ChunkedWriteHandler; import java.net.URI; Public class HttpDownloadClient {/** * Download HTTP resources Download HTTP resources from the server directly fill in the relative path of the file to be downloaded * (you are advised to partially filter special characters using only letters and digits. This may cause exceptions) * Download from the Internet. Enter the complete path * @param host IP address or domain name of the destination host * @param port Target host port * @Param URL file path * @param local Local storage path * @throws Exception */  public void connect(String host, int port, String url, final String local) throws Exception { EventLoopGroup workerGroup = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(workerGroup); b.channel(NioSocketChannel.class); b.option(ChannelOption.SO_KEEPALIVE, true); b.handler(new ChildChannelHandler(local)); // Start the client. ChannelFuture f = b.connect(host, port).sync(); URI uri = new URI(url); DefaultFullHttpRequest request = new DefaultFullHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.GET, uri.toASCIIString()); // Build the HTTP request request.headers().set(HttpHeaders.names.host, HOST); request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); request.headers().set(HttpHeaders.Names.CONTENT_LENGTH, request.content().readableBytes()); // Send an HTTP request f.channel().write(request); f.channel().flush(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); } } private class ChildChannelHandler extends ChannelInitializer<SocketChannel> { String local; public ChildChannelHandler(String local) { this.local = local; } @override protected void initChannel(SocketChannel ch) throws Exception {// The client receives an httpResponse response. .pipeline().addLast(new HttpResponseDecoder()); // HttpRequestEncoder ch. Pipeline ().addlast (new HttpRequestEncoder()); ch.pipeline().addLast(new ChunkedWriteHandler()); ch.pipeline().addLast(new HttpDownloadHandler(local)); } } public static void main(String[] args) throws Exception { HttpDownloadClient client = new HttpDownloadClient(); / / client. Connect (" 127.0.0.1 ", 9003, "/ file/prompt / 1. Doc", "1. Doc"); // client.connect("zlysix.gree.com", 80, "http://zlysix.gree.com/HelloWeb/download/20m.apk", "20m.apk"); client.connect("www.ghost64.com", 80, "http://www.ghost64.com/qqtupian/zixunImg/local/2017/05/27/1495855297602.jpg", "1495855297602.jpg"); }}Copy the code

The client end handler:

import java.io.File; import java.io.FileOutputStream; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.codec.http.HttpContent; //import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.LastHttpContent; import io.netty.util.internal.SystemPropertyUtil; /** * @Author:yangyue * @Description: * @Date: Created in 9:15 on 2017/5/28. */ public class HttpDownloadHandler extends ChannelInboundHandlerAdapter { private boolean  readingChunks = false; Private FileOutputStream fOutputStream = null; Private File localfile = null; Private String local = null; Private int succCode; Public HttpDownloadHandler(String local) {this.local = local; } @Override public void channelRead(ChannelHandlerContext ctx, Object MSG) throws Exception {if (MSG instanceof HttpResponse) {// Response header HttpResponse response = (HttpResponse) msg; succCode = response.getStatus().code(); if (succCode == 200) { setDownLoadFile(); // set download file readingChunks = true; } // System.out.println("CONTENT_TYPE:" // + response.headers().get(HttpHeaders.Names.CONTENT_TYPE)); } if (MSG instanceof HttpContent) {if (MSG instanceof HttpContent) {chunk = (HttpContent) MSG; if (chunk instanceof LastHttpContent) { readingChunks = false; } ByteBuf buffer = chunk.content(); byte[] dst = new byte[buffer.readableBytes()]; if (succCode == 200) { while (buffer.isReadable()) { buffer.readBytes(dst); fOutputStream.write(dst); buffer.release(); } if (null ! = fOutputStream) { fOutputStream.flush(); } } } if (! readingChunks) { if (null ! = fOutputStream) { System.out.println("Download done->"+ localfile.getAbsolutePath()); fOutputStream.flush(); fOutputStream.close(); localfile = null; fOutputStream = null; } ctx.channel().close(); }} /** * Configure local parameters, Prepare to download */ private void setDownLoadFile() throws Exception {if (null == fOutputStream) {local = SystemPropertyUtil.get("user.dir") + File.separator +local; //System.out.println(local); localfile = new File(local); if (! localfile.exists()) { localfile.createNewFile(); } fOutputStream = new FileOutputStream(localfile); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {system.out.println (" pipeline Exception: "+ cause.getMessage()); cause.printStackTrace(); ctx.channel().close(); }}Copy the code

Here I put the client is the network connection, download is a picture, start the server and client can see the picture is downloaded to the root directory of the project.

Recent hot articles recommended:

1.1,000+ Java Interview Questions and Answers (2021)

2. I finally got the IntelliJ IDEA activation code thanks to the open source project. How sweet!

3. Ali Mock is officially open source, killing all Mock tools on the market!

4.Spring Cloud 2020.0.0 is officially released, a new and disruptive version!

5. “Java Development Manual (Songshan version)” the latest release, quick download!

Feel good, don’t forget to click on + forward oh!