分片上传必须本地做md5校验以实现精准断点重传,而非防篡改;需流式读取指定偏移与长度,用tiny-md5等轻量库逐块update计算,注意二进制打开、seekg检查及hex小写转换。

为什么分片上传必须在本地做 MD5 校验
不是为了“防篡改”这种虚的,而是因为 HTTP 重试 + 分片拼接后,哪怕只有一片传错,整个文件就废了。服务端通常只校验最终合并后的完整 MD5,发现不一致只能让你全重传——断点上传的意义直接归零。
本地每片算 MD5,上传时带上 md5sum 字段,服务端收到立刻比对,错哪片就重传哪片,这才是真断点。
用 C++ 计算单个文件分片的 MD5(跨平台可跑)
别碰 OpenSSL 的 C API,初始化麻烦、错误码难 debug;用 std::ifstream 配合现成的轻量 MD5 实现最稳。推荐直接嵌入 md5.h / md5.cpp(如 tiny-md5),它只有两个函数:md5(std::string) 和 md5(std::istream&)。
分片校验的关键是:不能把整文件读进内存再切,得流式读指定偏移+长度。
实操建议:
- 用
std::ifstream打开文件,seekg(offset, std::ios::beg)跳到分片起始位置 - 分配固定大小 buffer(如 8192 字节),循环
read()直到读满分片长度或 EOF - 把每次
read()的数据喂给md5对象的update()方法(不是一次性传 whole chunk) - 最后调用
final()得到 16 字节二进制结果,再转成 32 位小写 hex 字符串(0123456789abcdef)
示例关键片段:
std::ifstream file("video.mp4", std::ios::binary);
file.seekg(1048576); // 第二片起始(1MB)
char buf[8192];
MD5 md5;
size_t remain = 1048576; // 当前分片大小
while (remain > 0 && file.good()) {
size_t to_read = std::min(remain, sizeof(buf));
file.read(buf, to_read);
size_t actual = file.gcount();
if (actual > 0) md5.update(buf, actual);
remain -= actual;
}
std::string hex = md5.final(); // 返回 "e3b0c44298fc1c149afbf4c8996fb924"
分片大小设多少?和 MD5 校验有啥隐性关系
不是越大越好,也不是越小越准。常见设 1–5MB,原因很实际:
HTTP 客户端(如 libcurl)默认缓冲区约 16KB,分片远大于此才能摊薄 TCP 握手/SSL 开销;但太大又导致单片校验耗时长(尤其机械盘),出错重传成本高。
重点注意:
— 分片边界必须对齐:不能在 UTF-8 字符中间切,也不能在 ZIP 文件的 central directory 处切——但你只是传二进制文件,所以只要按字节切就行,无需解析内容
— MD5 本身不关心分片大小,但你的 buffer 大小会影响 I/O 效率:小于 4KB 容易触发磁盘寻道,大于 64KB 在多数 SSD 上已无明显收益
— 如果文件总大小不是分片大小的整数倍,最后一片必然更小,MD5 计算逻辑必须能处理任意长度(上面示例已覆盖)
容易被忽略的三个坑
真实项目里,90% 的“校验失败但看不出错在哪”都栽在这三处:
-
std::ifstream必须带std::ios::binary标志,否则 Windows 下遇到\r\n会被悄悄转成\n,MD5 全错 - 计算前没检查
file.seekg()是否成功——如果 offset 超过文件大小,seekg失败但不报异常,后续read()返回 0 字节,算出的 MD5 是空字符串的哈希(d41d8cd98f00b204e9800998ecf8427e) - hex 字符串生成时用了大写 A-F,而服务端比对的是小写——
MD5值本身区分大小写,必须统一转小写再发
最后一句:MD5 不是安全算法,但断点上传场景下它只用来检测传输错误,不是防恶意篡改,够用。真要上 SHA256,流程一模一样,换库、换字段名就行,别在哈希选型上卡住进度。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











