Netty-based file transfer

Netty-based file transfer

Recently, netty has been used to transfer files, establish a channel and then read and write files. It mainly uses the RandomAccessFile class, which can read and write files at specified locations and specified byte sizes. The following is the specific implementation idea:

  • The server is used to send the FileUploadFile Java object, which includes files, file information, etc., and uses RandomAccessFile to read the file, each time 1024b (1kb), and sends it in segments. The first segment is sent when the first connection is made, and then the client Returns the received byte size, and then reads the next fragment from this position. If the remaining fragment is less than 1kb, it will be sent, and if it is larger, only 1kb will be sent.
  • The client is used to receive the FileUploadFile Java object. After parsing the object, use RandomAccessFile to write the file, write 1kb each time, then re-determine the location, and then return the data to the server.
    The code implements
    the FileUploadFile class to store file objects
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FileUploadFile implements Serializable {
    
     //Serializable序列化不能漏
    /* 文件 */
    private File file;
    /* 文件名称 */
    private String fileName;
    /* 开始位置 */
    private int startPos;
    /* 文件字节数组 */
    private byte[] body;
    /* 结束位置 */
    private int endPos;

}

Server connection Server class

package com.demo.netty.server;

import com.demo.LoggerFactory;
import com.demo.netty.file.FileUploadFile;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
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;

import java.io.File;

public class Server {
    
    

    private static final File file = new File("D:\\temp\\res.txt");
    private static final int port = 8888;

    public static void main(String[] args)throws Exception {
    
    
        FileUploadFile fileUploadFile = new FileUploadFile();
        fileUploadFile.setFile(file);
        fileUploadFile.setFileName(file.getName());
        new Server().serverStart(port,fileUploadFile );
    }

    public void serverStart(int port, final FileUploadFile fileUploadFile) throws InterruptedException {
    
    
        //1 第一个线程组 是用于接收client端的连接
        EventLoopGroup bossgroup = new NioEventLoopGroup();
        //2 第二个线程组 用于实际的业务开发处理操作
        EventLoopGroup workgroup = new NioEventLoopGroup();
        //3 创建一个辅助类Bootstrap,就是对我们的server进行一系列的配置
        ServerBootstrap sb = new ServerBootstrap();
        //4 把两个线程组添加进来
        sb.group(bossgroup,workgroup)
                //指定使用NioServerSocketChannl这种类型的通道
                .channel(NioServerSocketChannel.class)
                //一定要使用childHandler去绑定具体的事件处理器
                .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 ServerHandler(fileUploadFile));
                    }
                });
        //绑定指定的端口 进行监听
        LoggerFactory.log.info("启动netty Server");
        ChannelFuture f = sb.bind(port).sync();

        f.channel().closeFuture().sync();
        //关闭线程
        bossgroup.shutdownGracefully();
        workgroup.shutdownGracefully();
    }
}

The server handles the ServerHandler class

package com.demo.netty.server;
import com.demo.netty.file.FileUploadFile;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;

import java.io.RandomAccessFile;
import java.net.InetSocketAddress;
@Slf4j
public class ServerHandler extends ChannelInboundHandlerAdapter {
    
    
    private int total; //总包数量
    private int row;//剩余包数
    private int byteRead; //读取结束位置
    private volatile int start = 0; //开始位置
    private volatile int lastLength = 0;//文件读取长度
    public RandomAccessFile randomAccessFile;
    private FileUploadFile fileUploadFile;

    public ServerHandler(FileUploadFile fileUploadFile) {
    
    
        if (fileUploadFile.getFile().exists()){
    
    
            if (!fileUploadFile.getFile().isFile()){
    
    
                log.warn("this is not a file");
                return;
            }
        }
        this.fileUploadFile = fileUploadFile;
    }

    /*
    * 客户端连接会触发该方法
    * */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
    
    
        InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
        String ip = remoteAddress.getAddress().getHostAddress();
        log.info("有客户端连接,ip地址为:{}",ip);
        log.info("开始发送FileUploadFile 对象");
        try{
    
    
            randomAccessFile = new RandomAccessFile(fileUploadFile.getFile(),"r");//文件只读,不存在不创建,抛出异常
            randomAccessFile.seek(fileUploadFile.getStartPos());//开始位置初始为0
            lastLength =1024;
            if(((randomAccessFile.length())%lastLength)!=0){
    
    
                total = (int) (randomAccessFile.length()/lastLength+1);
            }else {
    
    
                total = (int) (randomAccessFile.length()/lastLength);
            }
            row = total;
            byte[] body = new byte[lastLength];//创建一个字节长度为1024数组
            if ((byteRead = randomAccessFile.read(body))!=-1){
    
    
                fileUploadFile.setEndPos(byteRead);//存入结束位置
                fileUploadFile.setBody(body);//存入文件字节
                ctx.writeAndFlush(fileUploadFile);//发送对象
            }
            row--;
            log.info("第一段文件读取完毕!");
            log.info("文件总长度:[{}],剩余长度:[{}],发送长度:[{}]",randomAccessFile.length(),randomAccessFile.length()-byteRead,byteRead);
            log.info("总包数:[{}],剩余包数:[{}]",total,row);

        }catch (Exception e){
    
    
            e.printStackTrace();
        }
    }


    /*
    * 接收信息
    * */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{
    
    
            if (msg instanceof Integer){
    
    
                start  = (int) msg;
                if (start!=-1){
    
    
                    randomAccessFile = new RandomAccessFile(fileUploadFile.getFile(),"r");
                    randomAccessFile.seek(start); //定位文件位置
                    int  a = (int) (randomAccessFile.length()-start);
                    if (a < lastLength){
    
     //剩余长度小于1024一次发完
                        lastLength = a;
                    }
                    byte[] body = new byte[lastLength];
                    if ((byteRead = randomAccessFile.read(body))!=-1 && a>0){
    
    
                        fileUploadFile.setBody(body);
                        fileUploadFile.setEndPos(byteRead);
                        ctx.writeAndFlush(fileUploadFile);
                        row--;
                        log.info("文件总长度:[{}],剩余长度:[{}],发送长度:[{}]",randomAccessFile.length(),a,byteRead);
                        log.info("总包数:[{}],剩余包数:[{}]",total,row);
                    }else {
    
    
                        log.info("文件已全部发送完毕,即将关闭通道");
                        randomAccessFile.close();
                        ctx.flush();
                        ctx.close();
                    }
                }
            }
    }

    /*
    * 连接异常触发
    * */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    
    
        cause.printStackTrace();
        ctx.close();
    }
}

Client connection class Client

package com.demo.netty.client;

import com.demo.LoggerFactory;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
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;

/*
* netty 客户端
* */
public class Client {
    
    

    private static final String host = "127.0.0.1";

    public static void main(String[] args) throws Exception {
    
    
        new Client().clientStart(8888);
    }

    public void clientStart(int port) throws InterruptedException {
    
    
        EventLoopGroup workgroup = new NioEventLoopGroup();
        Bootstrap b = new Bootstrap();
        b.group(workgroup);
        b.channel(NioSocketChannel.class);
        b.handler(new ChannelInitializer<Channel>() {
    
    
            @Override
            protected void initChannel(Channel ch) {
    
    
                ch.pipeline().addLast(new ObjectEncoder());
                ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.weakCachingConcurrentResolver(null)));
                ch.pipeline().addLast(new ClientHandler());
            }
        });
        ChannelFuture cf1 = b.connect(host,port).sync();

        LoggerFactory.log.info("Netty Client start");

        cf1.channel().closeFuture().sync();
        workgroup.shutdownGracefully();
    }
}

Client processing class ClientHandler

package com.demo.netty.client;


import com.demo.netty.file.FileUploadFile;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.RandomAccessFile;
@Slf4j
public class ClientHandler extends ChannelInboundHandlerAdapter {
    
    

    private int byteRead;
    private volatile int start = 0; //开始位置
    private static final String file_path = "D:\\Download\\";

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
    
    
        log.info("已连接到服务器");
    }


    @Override
    public void channelRead(ChannelHandlerContext cxt,Object msg) throws Exception {
    
    
       if (msg instanceof FileUploadFile){
    
    
            log.info("接收到服务端的FileUploadFile对象");
            FileUploadFile fileUploadFile = (FileUploadFile) msg;
            byte[] body = fileUploadFile.getBody(); //文件内容转换为字节流
            byteRead = fileUploadFile.getEndPos(); //获取文件上次读取的位置
            String file_name = fileUploadFile.getFileName();//获取文件名称
            String path = file_path+file_name; //文件路径
            File file = new File(path);//根据文件路径创建文件
            RandomAccessFile randomAccessFile = new RandomAccessFile(file,"rw");//读写操作,如果文件不存在,则自动创建文件
            randomAccessFile.seek(start); //移动至start位置开始写入文件
            randomAccessFile.write(body);
            start+=byteRead;
            cxt.writeAndFlush(start);//向服务端发送数据,下次开始的位置
            log.info("写入完毕,文件路径为:{}",path);
       }
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    
    
        cause.printStackTrace();
        ctx.close();
    }

}

Start the main program of the Server class first, and then start the main program of the Client class, and then it can run. The path can be modified by yourself. The running screenshot is as follows:
Server client
insert image description here
client
insert image description here

The last maven dependency:

		<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.12.Final</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.dyuproject.protostuff</groupId>
            <artifactId>protostuff-core</artifactId>
            <version>1.0.8</version>
        </dependency>
        <dependency>
            <groupId>com.dyuproject.protostuff</groupId>
            <artifactId>protostuff-runtime</artifactId>
            <version>1.0.8</version>
        </dependency>

Guess you like

Origin blog.csdn.net/qq_44874270/article/details/119132425