这篇文章主要讲解了“怎么使用java Netty实现传输文件、分片发送、断点续传”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么使用java Netty实现传输文件、分片发送、断点续传”吧!
1、jdk1.8【jdk1.7以下只能部分支持netty】
2、Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】
itstack-demo-netty-2-04└── src ├── main │ └── java │ └── org.itstack.demo.netty │ ├── client │ │ ├── MyChannelInitializer.java │ │ ├── MyClientHandler.java │ │ └── NettyClient.java │ ├── codec │ │ ├── ObjDecoder.java │ │ └── ObjEncoder.java │ ├── domain │ │ ├── Constants.java │ │ ├── FileBurstData.java │ │ ├── FileBurstInstruct.java │ │ ├── FileDescInfo.java │ │ └── FileTransferProtocol.java │ ├── server │ │ ├── MyChannelInitializer.java │ │ ├── MyServerHandler.java │ │ └── NettyServer.java │ └── util │ ├── CacheUtil.java │ ├── FileUtil.java │ ├── MsgUtil.java │ └── SerializationUtil.java │ └── test └── java └── org.itstack.demo.test ├── ApiTest.java ├── NettyClientTest.java └── NettyServerTest.java
演示部分重点代码块,完整代码下载关注公众号;bugstack虫洞栈
client/MyClientHandler.java *文件客户端;channelRead处理文件协议,其中模拟传输过程中断,场景测试可以注释掉
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //数据格式验证 if (!(msg instanceof FileTransferProtocol)) return; FileTransferProtocol fileTransferProtocol = (FileTransferProtocol) msg; //0传输文件'请求'、1文件传输'指令'、2文件传输'数据' switch (fileTransferProtocol.getTransferType()) { case 1: FileBurstInstruct fileBurstInstruct = (FileBurstInstruct) fileTransferProtocol.getTransferObj(); //Constants.FileStatus {0开始、1中间、2结尾、3完成} if (Constants.FileStatus.COMPLETE == fileBurstInstruct.getStatus()) { ctx.flush(); ctx.close(); System.exit(-1); return; } FileBurstData fileBurstData = FileUtil.readFile(fileBurstInstruct.getClientFileUrl(), fileBurstInstruct.getReadPosition()); ctx.writeAndFlush(MsgUtil.buildTransferData(fileBurstData)); System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " bugstack虫洞栈客户端传输文件信息。FILE:" + fileBurstData.getFileName() + " SIZE(byte):" + (fileBurstData.getEndPos() - fileBurstData.getBeginPos())); break; default: break; } /**模拟传输过程中断,场景测试可以注释掉 * */ System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " bugstack虫洞栈客户端传输文件信息[主动断开链接,模拟断点续传]"); ctx.flush(); ctx.close(); System.exit(-1);}
domain/FileBurstData.java *文件分片数据块
/** * 文件分片数据 * 虫洞栈:https://bugstack.cn * 公众号:bugstack虫洞栈 {获取学习源码} * 虫洞群:①群5398358 ②群5360692 * Create by fuzhengwei on 2019 */public class FileBurstData { private String fileUrl; //客户端文件地址 private String fileName; //文件名称 private Integer beginPos; //开始位置 private Integer endPos; //结束位置 private byte[] bytes; //文件字节;再实际应用中可以使用非对称加密,以保证传输信息安全 private Integer status; //Constants.FileStatus {0开始、1中间、2结尾、3完成} ... get/set}
domain/FileBurstInstruct.java *文件分片指令
/** * 文件分片指令 * 虫洞栈:https://bugstack.cn * 公众号:bugstack虫洞栈 {获取学习源码} * 虫洞群:①群5398358 ②群5360692 * Create by fuzhengwei on @2019 */public class FileBurstInstruct { private Integer status; //Constants.FileStatus {0开始、1中间、2结尾、3完成} private String clientFileUrl; //客户端文件URL private Integer readPosition; //读取位置 ... get/set}
domain/FileDescInfo.java *文件传输信息
/** * 文件描述信息 * 虫洞栈:https://bugstack.cn * 公众号:bugstack虫洞栈 {获取学习源码} * 虫洞群:①群5398358 ②群5360692 * Create by fuzhengwei on @2019 */public class FileDescInfo { private String fileUrl; private String fileName; private Long fileSize; ... get/set}
domain/FileTransferProtocol.java *文件传输协议
/** * 文件传输协议 * 虫洞栈:https://bugstack.cn * 公众号:bugstack虫洞栈 {获取学习源码} * 虫洞群:5360692 * Create by fuzhengwei on @2019 */public class FileTransferProtocol { private Integer transferType; //0请求传输文件、1文件传输指令、2文件传输数据 private Object transferObj; //数据对象;(0)FileDescInfo、(1)FileBurstInstruct、(2)FileBurstData ... get/set}
serverMyServerHandler.java *文件服务端;channelRead处理文件协议,并包含了保存续传信息,用于断点续传
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //数据格式验证 if (!(msg instanceof FileTransferProtocol)) return; FileTransferProtocol fileTransferProtocol = (FileTransferProtocol) msg; //0传输文件'请求'、1文件传输'指令'、2文件传输'数据' switch (fileTransferProtocol.getTransferType()) { case 0: FileDescInfo fileDescInfo = (FileDescInfo) fileTransferProtocol.getTransferObj(); //断点续传信息,实际应用中需要将断点续传信息保存到数据库中 FileBurstInstruct fileBurstInstructOld = CacheUtil.burstDataMap.get(fileDescInfo.getFileName()); if (null != fileBurstInstructOld) { if (fileBurstInstructOld.getStatus() == Constants.FileStatus.COMPLETE) { CacheUtil.burstDataMap.remove(fileDescInfo.getFileName()); } //传输完成删除断点信息 System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " bugstack虫洞栈服务端,接收客户端传输文件请求[断点续传]。" + JSON.toJSONString(fileBurstInstructOld)); ctx.writeAndFlush(MsgUtil.buildTransferInstruct(fileBurstInstructOld)); return; } //发送信息 FileTransferProtocol sendFileTransferProtocol = MsgUtil.buildTransferInstruct(Constants.FileStatus.BEGIN, fileDescInfo.getFileUrl(), 0); ctx.writeAndFlush(sendFileTransferProtocol); System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " bugstack虫洞栈服务端,接收客户端传输文件请求。" + JSON.toJSONString(fileDescInfo)); break; case 2: FileBurstData fileBurstData = (FileBurstData) fileTransferProtocol.getTransferObj(); FileBurstInstruct fileBurstInstruct = FileUtil.writeFile("E://", fileBurstData); //保存断点续传信息 CacheUtil.burstDataMap.put(fileBurstData.getFileName(), fileBurstInstruct); ctx.writeAndFlush(MsgUtil.buildTransferInstruct(fileBurstInstruct)); System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " bugstack虫洞栈服务端,接收客户端传输文件数据。" + JSON.toJSONString(fileBurstData)); //传输完成删除断点信息 if (fileBurstInstruct.getStatus() == Constants.FileStatus.COMPLETE) { CacheUtil.burstDataMap.remove(fileBurstData.getFileName()); } break; default: break; }}
util/FileUtil.java *文件读写工具,分片读取写入处理类
/** * 文件读写工具 * 虫洞栈:https://bugstack.cn * 公众号:bugstack虫洞栈 {获取学习源码} * 虫洞群:5360692 * Create by fuzhengwei on @2019 */public class FileUtil { public static FileBurstData readFile(String fileUrl, Integer readPosition) throws IOException { File file = new File(fileUrl); RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");//r: 只读模式 rw:读写模式 randomAccessFile.seek(readPosition); byte[] bytes = new byte[1024]; int readSize = randomAccessFile.read(bytes); if (readSize <= 0) { randomAccessFile.close(); return new FileBurstData(Constants.FileStatus.COMPLETE);//Constants.FileStatus {0开始、1中间、2结尾、3完成} } FileBurstData fileInfo = new FileBurstData(); fileInfo.setFileUrl(fileUrl); fileInfo.setFileName(file.getName()); fileInfo.setBeginPos(readPosition); fileInfo.setEndPos(readPosition + readSize); //不足1024需要拷贝去掉空字节 if (readSize < 1024) { byte[] copy = new byte[readSize]; System.arraycopy(bytes, 0, copy, 0, readSize); fileInfo.setBytes(copy); fileInfo.setStatus(Constants.FileStatus.END); } else { fileInfo.setBytes(bytes); fileInfo.setStatus(Constants.FileStatus.CENTER); } randomAccessFile.close(); return fileInfo; } public static FileBurstInstruct writeFile(String baseUrl, FileBurstData fileBurstData) throws IOException { if (Constants.FileStatus.COMPLETE == fileBurstData.getStatus()) { return new FileBurstInstruct(Constants.FileStatus.COMPLETE); //Constants.FileStatus {0开始、1中间、2结尾、3完成} } File file = new File(baseUrl + "/" + fileBurstData.getFileName()); RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");//r: 只读模式 rw:读写模式 randomAccessFile.seek(fileBurstData.getBeginPos()); //移动文件记录指针的位置, randomAccessFile.write(fileBurstData.getBytes()); //调用了seek(start)方法,是指把文件的记录指针定位到start字节的位置。也就是说程序将从start字节开始写数据 randomAccessFile.close(); if (Constants.FileStatus.END == fileBurstData.getStatus()) { return new FileBurstInstruct(Constants.FileStatus.COMPLETE); //Constants.FileStatus {0开始、1中间、2结尾、3完成} } //文件分片传输指令 FileBurstInstruct fileBurstInstruct = new FileBurstInstruct(); fileBurstInstruct.setStatus(Constants.FileStatus.CENTER); //Constants.FileStatus {0开始、1中间、2结尾、3完成} fileBurstInstruct.setClientFileUrl(fileBurstData.getFileUrl()); //客户端文件URL fileBurstInstruct.setReadPosition(fileBurstData.getEndPos() + 1); //读取位置 return fileBurstInstruct; }}
util/MsgUtil.java *传输消息体构建工具类
/** * 消息构建工具 * 虫洞栈:https://bugstack.cn * 公众号:bugstack虫洞栈 {获取学习源码} * 虫洞群:5360692 * Create by fuzhengwei on @2019 */public class MsgUtil { /** * 构建对象;请求传输文件(客户端) * * @param fileUrl 客户端文件地址 * @param fileName 文件名称 * @param fileSize 文件大小 * @return 传输协议 */ public static FileTransferProtocol buildRequestTransferFile(String fileUrl, String fileName, Long fileSize) { FileDescInfo fileDescInfo = new FileDescInfo(); fileDescInfo.setFileUrl(fileUrl); fileDescInfo.setFileName(fileName); fileDescInfo.setFileSize(fileSize); FileTransferProtocol fileTransferProtocol = new FileTransferProtocol(); fileTransferProtocol.setTransferType(0);//0请求传输文件、1文件传输指令、2文件传输数据 fileTransferProtocol.setTransferObj(fileDescInfo); return fileTransferProtocol; } /** * 构建对象;文件传输指令(服务端) * @param status 0请求传输文件、1文件传输指令、2文件传输数据 * @param clientFileUrl 客户端文件地址 * @param readPosition 读取位置 * @return 传输协议 */ public static FileTransferProtocol buildTransferInstruct(Integer status, String clientFileUrl, Integer readPosition) { FileBurstInstruct fileBurstInstruct = new FileBurstInstruct(); fileBurstInstruct.setStatus(status); fileBurstInstruct.setClientFileUrl(clientFileUrl); fileBurstInstruct.setReadPosition(readPosition); FileTransferProtocol fileTransferProtocol = new FileTransferProtocol(); fileTransferProtocol.setTransferType(Constants.TransferType.INSTRUCT); //0传输文件'请求'、1文件传输'指令'、2文件传输'数据' fileTransferProtocol.setTransferObj(fileBurstInstruct); return fileTransferProtocol; } /** * 构建对象;文件传输指令(服务端) * * @return 传输协议 */ public static FileTransferProtocol buildTransferInstruct(FileBurstInstruct fileBurstInstruct) { FileTransferProtocol fileTransferProtocol = new FileTransferProtocol(); fileTransferProtocol.setTransferType(Constants.TransferType.INSTRUCT); //0传输文件'请求'、1文件传输'指令'、2文件传输'数据' fileTransferProtocol.setTransferObj(fileBurstInstruct); return fileTransferProtocol; } /** * 构建对象;文件传输数据(客户端) * * @return 传输协议 */ public static FileTransferProtocol buildTransferData(FileBurstData fileBurstData) { FileTransferProtocol fileTransferProtocol = new FileTransferProtocol(); fileTransferProtocol.setTransferType(Constants.TransferType.DATA); //0传输文件'请求'、1文件传输'指令'、2文件传输'数据' fileTransferProtocol.setTransferObj(fileBurstData); return fileTransferProtocol; }}
test/NettyServerTest.java *服务端启动器
/** * 虫洞栈:https://bugstack.cn * 公众号:bugstack虫洞栈 {获取学习源码} * 虫洞群:①群5398358 ②群5360692 * Create by fuzhengwei on 2019 */public class NettyServerTest { public static void main(String[] args) { //启动服务 new NettyServer().bing(7397); }}
test/NettyClientTest.java *客户端启动器
/** * 虫洞栈:https://bugstack.cn * 公众号:bugstack虫洞栈 {获取学习源码} * 虫洞群:①群5398358 ②群5360692 * Create by fuzhengwei on 2019 */public class NettyClientTest { public static void main(String[] args) { //启动客户端 ChannelFuture channelFuture = new NettyClient().connect("127.0.0.1", 7397); //文件信息{文件大于1024kb方便测试断点续传} File file = new File("C:\\Users\\fuzhengwei\\Desktop\\测试传输文件.rar"); FileTransferProtocol fileTransferProtocol = MsgUtil.buildRequestTransferFile(file.getAbsolutePath(), file.getName(), file.length()); //发送信息;请求传输文件 channelFuture.channel().writeAndFlush(fileTransferProtocol); }}
启动NettyServerTest *默认接收地址为E盘根目录
启动NettyClientTest *设置传输文件
文件传输结果
服务端执行结果
itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码}链接报告开始链接报告信息:有一客户端链接到本服务端。channelId:3a1df8c1链接报告IP:127.0.0.1链接报告Port:7397链接报告完毕2019-08-04 19:46:46 bugstack虫洞栈服务端,接收客户端传输文件请求。{"fileName":"测试传输文件.rar","fileSize":1400,"fileUrl":"C:\\Users\\fuzhengwei1\\Desktop\\测试传输文件.rar"}2019-08-04 19:46:46 bugstack虫洞栈服务端,接收客户端传输文件数据。{"beginPos":0,"bytes":"UmFyIRoHAM+QcwAADQAAAAAAAAC4C3SgkkkAFAUAAIjDEQACJRsHe0WECE8dMyQAIAAAALLiytS0q8rkzsS8/i50eHQAbWpL1YsgT5OPoIdl9k4udAB4dACwS2heCZgVEQzPzUEXfAnhs2R75rhNbCQhNE3uMY4EBkBqQJ45izS4lFGujEk08xLGuhp4sSUSbzEscRICakGyOdARhIE6GEPucySJpY5kQ/Cq28ur4XdfH/j1V8UVoo5X5V+B3dl2f8qvvoxd3t6GPv8HZ7dXs+98XT6uJ0Oj8GZ4c6tvV6vzV865ka375utod+9i+pX/O1Uu1tT76tT38TE+Hq+tzud6Of9Xo9T0/S/xytLm12v4NWztfhnda3lbevs7dnXsWL1vT3Kte91triqYuHW/9bf3WPnjq5r5savbHd67V8Nu6r5+lmZtrP0eO63Ba+upVuWtf7bvByg2/0w+5hz8ru14ND5ex/Odw4F7uRWrYedwU2tXmw5m1u+S7lWdjvTq5e/Kv+1apZxrdjT2dHizdHDrlrH1cvkbrWe5k97u6WRnXdviw6zkvc3cD9TOt7+7W9z2/Ys+Sx9VTPwGeYLmrz+h8fQt5u5v8/3fZ5sXKnc4MOT1+n0upicmOVDT86GfY4bPf5vN7XSxMT5Mnsdry8e84///+quHp9l19fRz8vkds+O7qb+9pWe1WvXdb7NWza3vNO3V3cZZ2rPDr1svHwO3Nq14sBuZu3P1zOvuWP++s8ex9O95e5U/vW9/F7Jtb+p+PGeRtzlg8VfG5t5TyAAAAAAAAAAAAAAAARu1PHU9QX3wAAAAAAAAAAAAAAAiiXyXwAAAAAAAAAAAAAAAiiXyXwAAAAAAAAAAAAAAAiiXyXwAAAAAAAAAAAAAAAiiXyXwAEbDTyAAAAAAAAAAAAAEctTx1PUF98AAAAAAAAAAAAAAAIol8l8AAAAAAAAAAAAAAAIol8l8AAAAAAAAAAAAAAAIol8l8AAAAAAAAAAAAAAAIol8l8AAAABHx08gAAAAAAAAAAj108dT1BffAAAAAAAAAAAAAAACKJfJfAAAAAAAAAAAAAAACKJfJfAAAAAAAAAAAAAAACKJfJfAAAAAAAAAAAAAAACKJfJfAAAAAAAAgankAAAAAAAEZGnjqeoL74AAAAAAAAAAAAAAARRL5L4AAAAAAAAAAAAAAARRL5L4AAAAAAAAAAAAAAARRL5L4AAAAAAAAAAAAAAARRL5L4AAAAAAAAAAEYinkAAAAEH08dT1BffAAAAAAAAAAAAAAACKJfJfAAAAAAAAAAAAAAACKJfJfAAAAAAAAAAAAAAACKJfJfAAAAAAAAAAAAAAACKJfJfAAAAAAAAAAA==","endPos":1024,"fileName":"测试传输文件.rar","fileUrl":"C:\\Users\\fuzhengwei1\\Desktop\\测试传输文件.rar","status":1}2019-08-04 19:46:46 bugstack虫洞栈服务端,接收客户端传输文件数据。{"beginPos":1025,"bytes":"AAI8VPIAEfZTx1PUF98AAAAAAAAAAAAAAAIol8l8AAAAAAAAAAAAAAAIol8l8AAAAAAAAAAAAAAAIol8l8AAAAAAAAAAAAAAAIol8l8AAAAAAAAAAAAAAAIol8l8R+dPIAAAAAAAAAAAAAAAjt08dT1BffAAAAAAAAAAAAAAACKJfJfAAAAAAAAAAAAAAACKJfJfAAAAAAAAAAAAAAACKJfJfAAAAAAAAAAAAAAACKJfJfAAAIWp5AAAAAAAAAAAAEYGnjqeoL74AAAAAAAAAAAAAAARRL5L4AAAAAAAAAAAAAAARRL5L4AAAAAAAAAAAAAAARRL5L4AAAAAAAAAAAAAAARRL5L4AAAAABGYp5AAAAAAAAAEc/Tx1PUF98AAAAAAAAIyFPIAAAAAACB6eOp6gvvgAAAAAAAAAARiKeQAAAAQfTx1PUF98AAAAAAAAAAAAAjAU8gACF6eOp6gvvgAAAAAAAAAAAAAABFH9IDEPXsAQAcA","endPos":1400,"fileName":"测试传输文件.rar","fileUrl":"C:\\Users\\fuzhengwei1\\Desktop\\测试传输文件.rar","status":2}客户端断开链接/127.0.0.1:7397Process finished with exit code -1
客户端执行结果
itstack-demo-netty client start done. {关注公众号:bugstack虫洞栈,获取源码}链接报告开始链接报告信息:本客户端链接到服务端。channelId:71399d8c链接报告IP:127.0.0.1链接报告Port:54974链接报告完毕2019-08-04 19:46:46 bugstack虫洞栈客户端传输文件信息。FILE:测试传输文件.rar SIZE(byte):10242019-08-04 19:46:46 bugstack虫洞栈客户端传输文件信息。FILE:测试传输文件.rar SIZE(byte):375Process finished with exit code -1
感谢各位的阅读,以上就是“怎么使用java Netty实现传输文件、分片发送、断点续传”的内容了,经过本文的学习后,相信大家对怎么使用java Netty实现传输文件、分片发送、断点续传这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/itstack/blog/4409785