Netty 大文件传输

Netty 大文件传输Netty 大文件传输使用 netty 进行大文件传输 对文件大小没有限制实际上 传输的是文件分片 分片大小自定义整体思路客户端一连上服务器 即发送指定文件的文件分片服务器收到分片后 写入指定路径 并向客户端返回所收到字节数 即通知客户端下次从文件的此字节开始传输 客户端收到服务器的回应后 从指定字节开始创建下一个文件分片继续发送给服务器 若已到文件末尾 则发送 1 表示文件传输结束服务器循环步骤 2 直至收到客户端发来的 1 也回应给客户端 1 表示收到传输结束的通知客户端在收到 1 后 断开

Netty 大文件传输

使用netty进行大文件传输,对文件大小没有限制 实际上,传输的是文件分片,分片大小自定义 

整体思路

  1. 客户端一连上服务器,即发送指定文件的文件分片
  2. 服务器收到分片后,写入指定路径,并向客户端返回所收到字节数(即通知客户端下次从文件的此字节开始传输)
  3. 客户端收到服务器的回应后,从指定字节开始创建下一个文件分片 继续发送给服务器,若已到文件末尾,则发送-1表示文件传输结束
  4. 服务器循环步骤2,直至收到客户端发来的-1,也回应给客户端-1表示收到传输结束的通知
  5. 客户端在收到-1后,断开与服务器的连接

代码实现

  • 完整代码

    github:https://github.com/StanAugust/NettyFileTransfer/tree/master

  • 主要代码
  1. 客户端
/ * @ClassName: ClientHandler * @Description: 客户端的处理器,在在client.ClientInitializer initChannel中被调用 * @author Stan * @date: 2020年3月24日 */ public class ClientHandler extends ChannelInboundHandlerAdapter{ 
    private static final Logger logger = Logger.getLogger(ClientHandler.class.getName()); private int byteRead; //一次读取的字节 private volatile int start = 0; //文件当前读取位置 private volatile int lastLength = 0; //单次文件传输剩余长度 private RandomAccessFile file; private FileConfig fc; public ClientHandler(FileConfig fc) { 
    if(fc.getFile().exists()) { 
    if(!fc.getFile().isFile()) { 
    logger.info("error:" + fc.getFile() + "is not a file!"); return; } } this.fc = fc; } / * @Description: * 连接一激活就向服务器发送文件,发送文件的函数在这里修改 * 若文件可一次传输完,则只调用本方法,否则调用一次本方法后,其余在channelRead中传输 * * @param ctx * @throws Exception * @see io.netty.channel.ChannelInboundHandlerAdapter#channelActive(io.netty.channel.ChannelHandlerContext) */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception{ 
    file = new RandomAccessFile(fc.getFile(), "r"); send0(ctx, start); } / * @Description: 服务器通知已接受到的字节数,客户端收到通知并传输剩余文件 * @param ctx * @param msg * @throws Exception * @see io.netty.channel.ChannelInboundHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object) */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
    //收到服务器发来的字节数 if(msg instanceof Integer) { 
    start = (Integer)msg; if(start != -1) { 
    //文件没有传完 logger.info("服务器已收到字节数:" + start); send0(ctx, start); }else { 
    file.close(); logger.info("服务器已接收全部文件"); // 服务器接收完文件就与客户端断开连接 ctx.close(); } } } / * @throws Exception * @Description: 具体处理发送 */ private void send0(ChannelHandlerContext ctx, int start) throws Exception { 
    // 文件没有传完 if (start != -1) { 
    file.seek(start); // 把文件的记录指针定位到start字节的位置。也就是说本次将从start字节开始读数据 int nowLength = (int) (file.length() - start); // 文件当前总剩余长度 int transferLength = FileConfig.getTransferLength(); // 自定义的单次传输长度 lastLength = nowLength<transferLength ? nowLength:transferLength; // 选取较短一方作为单次文件传输剩余长度 transfer(ctx, lastLength); } } / * @Description: 完成单次传输 * @param ctx * @param length 单次传输长度 * @throws Exception */ private void transfer(ChannelHandlerContext ctx, int length) throws Exception { 
    byte[] buf = new byte[length]; if ((byteRead = file.read(buf)) != -1 && length > 0) { 
    fc.setEndPos(byteRead); fc.setFileBuf(buf); } else { 
    fc.setEndPos(-1); //结束位置-1,表示文件传输结束 fc.setFileBuf(null); logger.info("文件已上传完毕"); } ctx.writeAndFlush(fc); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
    cause.printStackTrace(); ctx.close(); } } 
  1. 服务器
/ * @ClassName: ServerHandler * @Description: 服务器端的处理器,在server.ServerInitializer initChannel中调用 * @author Stan * @date: 2020年3月24日 */ public class ServerHandler extends ChannelInboundHandlerAdapter{ 
    private static final Logger logger = Logger.getLogger(ServerHandler.class.getName()); private int byteRead; private int start = 0; / * @Description: 服务器接收到消息后进入这个方法,接收文件的函数在这里修改 * @param ctx * @param msg * @throws Exception * @see io.netty.channel.ChannelInboundHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object) */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
    //如果传递过来的是文件或文件分片 if(msg instanceof FileConfig) { 
    FileConfig fc = (FileConfig)msg; byte[] fileBuf = fc.getFileBuf(); //接收到的文件字节数组 byteRead = fc.getEndPos(); //记录当前文件传输结束的位置 if(byteRead == -1) { 
    // 约定的结束的标志 fileEnd(ctx); }else { 
    if(byteRead > 0) { 
    // TODO 文件接收路径需要指定 RandomAccessFile file = new RandomAccessFile(new File("test2.txt"), "rw"); file.seek(start); //把文件的记录指针定位到start字节的位置。也就是说程序本次将从start字节开始写数据 file.write(fileBuf);//把传输过来的数据写进文件里 start += byteRead; //确保文件下次能从当前结束的地方继续读取 ctx.writeAndFlush(start); //向客户端通知下次从第start字节开始传输 file.close(); logger.info("服务器已接收字节数:" + start + ",客户端地址:" + ctx.channel().remoteAddress()); }else { 
    exceptionCaught(ctx, new Throwable("可读字节小于0")); } } } } / * @Description: 文件接收完毕 * @param ctx */ private void fileEnd(ChannelHandlerContext ctx) { 
    ctx.writeAndFlush(-1); logger.info("服务器接收文件完毕" + "\n文件来源:"+ ctx.channel().remoteAddress() + "\n文件大小:" + start + " 字节"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
    ctx.close(); } } 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/216497.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 上午11:43
下一篇 2026年3月18日 上午11:44


相关推荐

  • 配置静态路由,动态路由,默认路由模式_默认路由为网络和掩码

    配置静态路由,动态路由,默认路由模式_默认路由为网络和掩码一、什么是路由路由(routing)是指分组从源到目的地时,决定端到端路径的网络范围的进程[1]。路由工作在OSI参考模型第三层——网络层的数据包转发设备。路由器通过转发数据包来实现网络互连。虽然路由器可以支持多种协议(如TCP/IP、IPX/SPX、AppleTalk等协议),但是在我国绝大多数路由器运行TCP/IP协议。路由器通常连接两个或多个由IP子网或点到点协议标识的…

    2025年12月4日
    3
  • Spring Boot 使用 JAX-WS 调用 WebService 服务[通俗易懂]

    Spring Boot 使用 JAX-WS 调用 WebService 服务[通俗易懂]SpringBoot使用JAX-WS调用WebService服务1新建SpringBootMaven示例工程项目2自动生成JAX-WS代码除了CXF我们还可以使用SpringBoot自身默认的组件JAX-WS来实现WebService的调用。本项目源码github下载1新建SpringBootMaven示例工程项目注意:是用来…

    2022年7月15日
    43
  • hduoj 1034「建议收藏」

    hduoj 1034「建议收藏」CandySharingGameTimeLimit:2000/1000MS(Java/Others)    MemoryLimit:65536/32768K(Java/Others)TotalSubmission(s):5890    AcceptedSubmission(s):3580ProblemDescriptionAnumberof

    2025年6月6日
    7
  • N3K跨网段流量异常

    N3K跨网段流量异常

    2021年9月14日
    45
  • java开发面试自我介绍模板_java面试自我介绍3篇

    java开发面试自我介绍模板_java面试自我介绍3篇java 面试自我介绍 3 篇 java 面试自我介绍篇一 我叫赵 我的同学更都喜欢称呼我的英文名字 叫 六月的意思 是君的谐音 我来自安徽的市 在 21 年我以市全市第一名的成绩考上了大学 学的是计算机科学专业 不过 在大 我没法再像高中一样总是名列前茅了 到目前为止 我的综合学分排名是 4 左右 在专业课程方面 我 c 的编程能力比较强 一年以前就开始自学 java 在班级里是最早开始学 java 的 我参与过我们

    2026年3月18日
    2
  • 原备案在腾讯云 操作新增网站备案教程

    原备案在腾讯云 操作新增网站备案教程什么叫新增网站备案 备案主体已在腾讯云办理过备案 现要新增网站 则需进行新增网站 原备案在腾讯云 操作 备案准备 为了节约备案时间和顺利通过备案 建议提前了解备案流程 进行备案准备 因各地管局要求不同 需准备的材料也有所不同 建议提前了解各省 自治区 直辖市管局的备案要求 以及相关备案限制 备案要求 1 进行网站

    2026年3月19日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号