Netty实战应用学习笔记-中级拓展篇(四)

bugstack虫洞栈 | netty4.1案例文章列表汇总,跟着案例学Netty itstack-demo-netty netty4.x案例 微信公众号:bugstack虫洞栈,欢迎您的关注&获取更多案例与源码!博客栈:https://bugstack.cn 本专题针对netty4.x分为从;基础入门篇(九篇)、中级拓展篇(八篇)、高级应用篇(三章),由浅入深实现netty服务功能,介绍netty如何从上手到实战。 基础入门篇(九篇) netty案例,netty4.1... 阅读详情

📁 项目四:文件传输、分片发送、断点续传

项目概述

项目名称:itstack-demo-netty-2-04
核心功能:实现大文件分片传输和断点续传功能
技术要点:RandomAccessFile、分片传输、断点续传、协议设计

为什么需要这个项目?

实际问题:

  • 大文件一次性传输容易导致内存溢出
  • 网络不稳定时传输中断,需要重新开始
  • 无法显示传输进度,用户体验差
  • 文件传输失败后浪费时间和流量

解决方案:

  • ✅ 分片传输:每次传输100KB,避免内存溢出
  • ✅ 断点续传:记录传输位置,中断后继续传输
  • ✅ 进度监控:实时显示传输进度
  • ✅ RandomAccessFile:支持随机读写,实现断点续传

适用场景:

  • 网盘系统(百度网盘、阿里云盘)
  • 大文件上传下载(视频、软件安装包)
  • 视频流媒体传输
  • 分布式文件系统(HDFS、FastDFS)

核心知识点

1. 系统架构设计

传输协议设计:

FileTransferProtocol(文件传输协议)
├── transferType(传输类型)
│   ├── 0 - REQUEST(请求传输文件)
│   ├── 1 - INSTRUCT(文件传输指令)
│   └── 2 - DATA(文件传输数据)
└── transferObj(传输对象)
    ├── FileDescInfo(文件描述信息)- type=0
    ├── FileBurstInstruct(文件分片指令)- type=1
    └── FileBurstData(文件分片数据)- type=2
2. 核心领域对象

FileDescInfo.java - 文件描述信息

public class FileDescInfo {
    private String fileUrl;    // 文件路径
    private String fileName;   // 文件名
    private Long fileSize;     // 文件大小
}

FileBurstInstruct.java - 文件分片指令

public class FileBurstInstruct {
    private Integer status;         // 0开始、1中间、2结尾、3完成
    private String clientFileUrl;   // 客户端文件URL
    private Integer readPosition;   // 读取位置(断点续传的关键)
}

FileBurstData.java - 文件分片数据

public class FileBurstData {
    private String fileUrl;      // 客户端文件地址
    private String fileName;     // 文件名称
    private Integer beginPos;    // 开始位置
    private Integer endPos;      // 结束位置
    private byte[] bytes;        // 文件字节数据(100KB)
    private Integer status;      // 0开始、1中间、2结尾、3完成
}
3. RandomAccessFile核心技术

什么是RandomAccessFile?

RandomAccessFile是Java提供的支持随机访问文件的类,可以从文件的任意位置读写数据。

对比:

普通文件读取:像看书,只能从第一页顺序往后翻
RandomAccessFile:像电子书,可以直接跳到任意页码

分片读取文件:

public static FileBurstData readFile(String fileUrl, Integer readPosition) 
    throws IOException {
    File file = new File(fileUrl);
    RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
    
    // 1. 定位到指定位置(断点续传的关键)
    randomAccessFile.seek(readPosition);
    
    // 2. 读取100KB数据
    byte[] bytes = new byte[1024 * 100];  // 100KB缓冲区
    int readSize = randomAccessFile.read(bytes);
    
    // 3. 读取完成
    if (readSize <= 0) {
        randomAccessFile.close();
        return new FileBurstData(Constants.FileStatus.COMPLETE);
    }
    
    // 4. 构建数据对象
    FileBurstData fileInfo = new FileBurstData();
    fileInfo.setBeginPos(readPosition);
    fileInfo.setEndPos(readPosition + readSize);
    
    // 5. 处理最后一块数据(去掉空字节)
    if (readSize < 1024 * 100) {
        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;
}

为什么要去掉空字节?

假设文件总大小120KB:
- 第1次读取:0-100KB(完整100KB)✅
- 第2次读取:100-120KB(只有20KB)⚠️

如果不去掉空字节:
byte[100KB] = [20KB实际数据 + 80KB空字节]
写入文件时会多出80KB垃圾数据,导致文件损坏!❌

分片写入文件:

public static FileBurstInstruct writeFile(String baseUrl, 
                                         FileBurstData fileBurstData) 
    throws IOException {
    File file = new File(baseUrl + "/" + fileBurstData.getFileName());
    RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
    
    // 1. 定位到指定位置写入
    randomAccessFile.seek(fileBurstData.getBeginPos());
    randomAccessFile.write(fileBurstData.getBytes());
    randomAccessFile.close();
    
    // 2. 检查是否是最后一块
    if (Constants.FileStatus.END == fileBurstData.getStatus()) {
        return new FileBurstInstruct(Constants.FileStatus.COMPLETE);
    }
    
    // 3. 返回下一次读取指令
    FileBurstInstruct fileBurstInstruct = new FileBurstInstruct();
    fileBurstInstruct.setStatus(Constants.FileStatus.CENTER);
    fileBurstInstruct.setClientFileUrl(fileBurstData.getFileUrl());
    fileBurstInstruct.setReadPosition(fileBurstData.getEndPos() + 1);
    
    return fileBurstInstruct;
}
4. 完整传输流程

正常传输流程:

【客户端】                          【服务端】
    |                                  |
    | 1. 发送文件请求                   |
    |  (REQUEST + FileDescInfo)        |
    |--------------------------------->|
    |                                  | 2. 返回传输指令
    | 3. 接收指令                       |    (INSTRUCT + readPosition=0)
    |<---------------------------------|
    | 4. 读取文件(0-100KB)            |
    | 5. 发送数据                       |
    |  (DATA + FileBurstData)          |
    |--------------------------------->|
    |                                  | 6. 写入文件
    |                                  | 7. 保存断点信息
    |                                  | 8. 返回下一次指令
    | 9. 接收指令                       |    (INSTRUCT + readPosition=100KB)
    |<---------------------------------|
    |         ... 循环传输 ...          |

断点续传流程:

【客户端】                          【服务端】
    |                                  |
    | 1. 发送文件请求                   |
    |  (REQUEST + FileDescInfo)        |
    |--------------------------------->|
    |                                  | 2. 检查断点信息
    |                                  |    发现上次传到200KB
    |                                  | 3. 返回断点续传指令
    | 4. 接收指令                       |    (INSTRUCT + readPosition=200KB)
    |<---------------------------------|
    | 5. 从200KB位置继续读取            |
    |         ... 继续传输 ...          |
5. 断点续传实现

服务端保存断点信息:

// 缓存断点信息(实际应用中应保存到数据库)
public class CacheUtil {
    public static Map<String, FileBurstInstruct> burstDataMap = 
        new ConcurrentHashMap<>();
}

// 服务端Handler
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    FileTransferProtocol protocol = (FileTransferProtocol) msg;
    
    switch (protocol.getTransferType()) {
        case 0:  // REQUEST - 请求传输文件
            FileDescInfo fileDescInfo = (FileDescInfo) protocol.getTransferObj();
            
            // 检查是否有断点续传信息
            FileBurstInstruct oldInstruct = 
                CacheUtil.burstDataMap.get(fileDescInfo.getFileName());
            if (oldInstruct != null) {
                // 断点续传
                ctx.writeAndFlush(MsgUtil.buildTransferInstruct(oldInstruct));
                return;
            }
            
            // 新传输,从0开始
            ctx.writeAndFlush(MsgUtil.buildTransferInstruct(
                Constants.FileStatus.BEGIN, fileDescInfo.getFileUrl(), 0
            ));
            break;
            
        case 2:  // DATA - 接收文件数据
            FileBurstData fileBurstData = (FileBurstData) protocol.getTransferObj();
            
            // 写入文件
            FileBurstInstruct instruct = FileUtil.writeFile("E://", fileBurstData);
            
            // 保存断点信息(关键!)
            CacheUtil.burstDataMap.put(fileBurstData.getFileName(), instruct);
            
            // 返回下一次指令
            ctx.writeAndFlush(MsgUtil.buildTransferInstruct(instruct));
            break;
    }
}

项目总结

核心技术:

  • ✅ RandomAccessFile:支持随机读写
  • ✅ 分片传输:每次传输100KB
  • ✅ 断点续传:保存传输进度
  • ✅ 空字节处理:避免文件损坏

适用场景:

  • 网盘系统(百度网盘、阿里云盘)
  • 大文件上传下载
  • 视频流媒体传输
  • 分布式文件系统

netty案例,netty4.1中级拓展篇四《Netty传输文件、分片发送、断点续传》 前言介绍 1、在实际应用中我们经常使用到网盘服务,他们可以高效的上传下载较大文件。那么这些高性能文件传输服务,都需要实现的分片发送、断点续传功能。 2、在Java文件操作中有RandomAccessFile类,他可以支持文件的定位读取和写入,这样就满足了我们对文件分片的最基础需求。 3、Netty服务端启动后,可以向客户端发送文件传输指令;允许接收文件、控制读取位点、记录传输标记、文件接收完成。 ... 阅读详情

相关推荐

netty案例,netty4.1中级拓展篇一《Netty与SpringBoot整合》

前言介绍 在实际的开发中,我们需要对netty服务进行更多的操作,包括;获取它的状态信息、启动/停止、对客户端用户强制下线等等,为此我们需要把netty服务加入到web系统中,那么本章节介绍如何将Netty与SpringBoot整合。 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使...

bugstack虫洞栈 2642

Netty实战应用学习笔记-中级拓展篇(一)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 1072

netty案例,netty4.1中级拓展篇五《基于Netty搭建WebSocket,模仿微信聊天页面》

前言介绍 本章节我们模仿微信聊天页面,开发一个基于Netty搭建WebSocket通信案例。Netty的应用方面非常广;聊天、MQ、RPC、数据等等,在5G到来的时候更加需要大量数据传输,Netty的应用也会更加广阔。 1、这个案例使用SpringBoot+Netty+WebSocket搭建功能。 2、使用Netty提供的HttpServerCodec、HttpObjectAggregator、...

bugstack虫洞栈 3072

Netty实战应用学习笔记-中级拓展篇(六)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 630

netty案例,netty4.1中级拓展篇七《Netty请求响应同步通信》

前言介绍 在我们实现开发RPC框架的时候,需要选择socket的通信方式。而我们知道一般情况下socket通信类似与qq聊天,发过去消息,什么时候回复都可以。但是我们RPC框架通信,从感觉上类似http调用,需要在一定时间内返回,否则就会发生超时断开。这里我们选择netty作为我们的socket框架,采用future方式进行通信。 Dubbo:国内最早开源的 RPC 框架,由阿里巴巴公司开发并于...

bugstack虫洞栈 2265

netty案例,netty4.1中级拓展篇二《Netty使用Protobuf传输数据》

前言介绍 在netty数据传输过程中可以有很多选择,比如;字符串、json、xml、java对象,但为了保证传输的数据具备;良好的通用性、方便的操作性和传输的高性能,我们可以选择protobuf作为我们的数据传输格式。目前protobuf可以支持;C++、C#、Dart、Go、Java、Python等,也可以在JS里使用。知识点;ProtobufDecoder、ProtobufEncoder、Pr...

bugstack虫洞栈 2080

Dapper 快速上手指南:30 秒把数据库查询映射成 C 对象

Dapper 是 .NET 里一款轻量级 ORM / 对象映射器,核心就一件事:用最少的代码,把数据库查询结果映射成 C# 对象。它扩展 ADO.NET 的 `IDbConnection` 接口,不生成额外代码、不改变你的 SQL 写法,装好就能用。这篇 Dapper 教程带你从安装、配置到跑通第一次查询,顺带避开最常见的几个坑。 [![Dapper .NET 轻量 ORM 数据库映射项目主图]

gitblog_00439的博客 401

netty案例,netty4.1中级拓展篇三《Netty传输Java对象》

前言介绍 Netty在实际应用级开发中,有时候某些特定场景下会需要使用Java对象类型进行传输,但是如果使用Java本身序列化进行传输,那么对性能的损耗比较大。为此我们需要借助protostuff-core的工具包将对象以二进制形式传输并做编码解码处理。与直接使用protobuf二进制传输方式不同,这里不需要定义proto文件,而是需要实现对象类型编码解码器,用以传输自定义Java对象。 pro...

bugstack虫洞栈 2142

Netty实战应用学习笔记-中级拓展篇(十二)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 1087

netty案例,netty4.1中级拓展篇八《Netty心跳服务与断线重连》

前言介绍 在我们使用netty中,需要监测服务是否稳定以及在网络异常链接断开时候可以自动重连。需要实现监听;f.addListener(new MyChannelFutureListener()) 环境准备 1、jdk1.8【jdk1.7以下只能部分支持netty】 2、Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 代码示例 itstack-...

bugstack虫洞栈 2069

netty案例,netty4.1中级拓展篇十二《Netty流量整形数据流速率控制分析与实战》

作者:付政委 | 关注微信公众号:bugstack虫洞栈,获取专题案例源码 前言介绍 你是否使用过某盘,在前几年我们使用的时候速度飞快,上传下载嗖嗖嗖。但是近年来只要不办会员,速度慢的像蜗牛,当然人家也得赚钱我们能理解。那么这样的限速是怎么实现的呢,我们这个案例使用Netty的流量整形进行限速传输,测试过程中当你把流量整形功能去掉后你就是年费VIP。 流量整形(Traffic Shaping...

bugstack虫洞栈 3696

Netty实战应用学习笔记-中级拓展篇(九)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 1063

GD32F303驱动WS2812B灯带:用TIMER+PWM+DMA解放CPU,实现流畅灯效(附完整代码)

本文详细介绍了基于GD32F303的TIMER+PWM+DMA全自动控制方案,用于高效驱动WS2812B灯带。通过硬件外设协同工作,该方案不仅解放了CPU资源,还能实现复杂的灯光效果,如渐变、流水等。文章提供了完整的代码实现和性能优化技巧,适合嵌入式开发者参考。

weixin_42561143的博客 380

Netty实战应用学习笔记-中级拓展篇(八)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 640

摄像机实时接入分析平台视频分析网关AI智能分析智慧营业厅方案

通过智能化的改造,可以提高营业厅的运营效率,降低运营成本,同时提升客户体验和安全保障。

EasyDarwin开源社区技术博客 705

HarmonyOS ArkTS开发实战:用Axios封装一个通用的网络请求工具类(附完整代码)

本文详细介绍了如何在HarmonyOS ArkTS开发中利用Axios封装一个通用的网络请求工具类,涵盖架构设计、核心模块实现、UI状态联动及高级功能扩展。通过单例控制器、拦截器链配置和Token自动刷新机制,提升网络请求的稳定性和可维护性,适用于各类HarmonyOS应用开发场景。

weixin_30772105的博客 491

Netty实战应用学习笔记-中级拓展篇(十三)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 570

windows系统下,如何为comfyui如何安装triton

​ 有些加速插件、模型在安装的时候,比如说sageattention的安装,就需要提前安装triton这个包,不过这个包有点特殊,windows下略微不好装,经常会装失败。 🎠如何安装 之前也有人介绍过,要到github上找个wheel文件,找到对应的版本安装下就好了。 问题是:https://github.com/woct0rdho/triton-windows/releases 这个git地址上,已经找不到wheel文件了... ​

聚梦小课堂 4503

Netty实战应用学习笔记-中级拓展篇(五)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 636

使用pytorch构建LSTM_AE模型的两种方式

2.基本LSTM_Unit计算——基本LSTM_AE结构 3.自定义LSTM_Unit计算——自定义LSTM_AE结构 1.LSTM-Encoder-Decoder模型结构及简要解析 图基本lstm计算单元

qq_37840762的博客 7275

CTF实战:从ECDSA签名漏洞到flag获取

本文详细解析了ECDSA签名算法在CTF比赛中的实战应用,重点探讨了随机数重用和参数泄露等常见漏洞。通过PWNHUB六月赛的例题,展示了如何从签名数据中恢复私钥并获取flag,同时提供了防御措施和实战技巧,帮助参赛者提升解题能力。

weixin_42706667的博客 278

Netty实战应用学习笔记-中级拓展篇(七)

Netty实战应用学习笔记-中级拓展篇

java_rutu的博客 322

YOLO26涨点改进 | Conv篇/小目标改进 | AAAI 2025 FBRT-YOLO 两个轻量级FCM和MKP模块, 助力无人机航拍任务VisDrone、UAVDT和AI-TOD高效涨点

本文提出FBRT-YOLO26模型,通过引入两个轻量级模块FCM和MKP优化航拍图像检测性能。FCM模块通过特征互补映射解决小目标信息丢失问题,MKP模块利用多尺度卷积核增强目标感知能力。在VisDrone等数据集上的实验表明,该方法在精度和速度上均优于现有实时检测器。文章详细介绍了模块结构、核心代码实现及YAML配置文件修改方法,为航拍图像中的小目标检测提供了高效解决方案。该模型实现了检测精度与效率的良好平衡,具有实际应用价值。

Ai缝合怪、助力小伙伴高效发论文 369

Netty 4.1 使用 Protobuf 传输数据:itstack-demo-netty 中级拓展篇二实战解析

本篇技术指南以 CodeGuide 开源仓库中 itstack-demo-netty 中级拓展篇二的完整案例为蓝本,讲解如何在 Netty 4.1 服务端与客户端之间使用 Google Protocol Buffers(Protobuf)作为数据传输格式,涵盖 `MsgInfo.proto` 协议定义、protoc 编译、`ProtobufDecoder` / `ProtobufEncoder`

gitblog_00156的博客 248

RK3588 hdmiout调试(一)

RK3588 hdmiout调试(一)

目前从事linux驱动开发,多交流多学习吧 5171

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

上一篇: Netty实战应用学习笔记-中级拓展篇(三)
下一篇: Netty实战应用学习笔记-中级拓展篇(五)
不早睡不改名@
博客等级 码龄5年 536粉丝 306原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值