应使用io.ReadAt+io.Copy流式分片处理大文件,避免os.ReadFile导致OOM;分片大小设4MB,每片独立计算SHA256哈希并存为[32]byte,元数据记录file_id→chunk哈希映射,RPC需导出方法并用gob编码,HTTP上传须自定义handler解析multipart或raw body。

用 io.ReadAt + io.Copy 边读边切,不爆内存
直接 os.ReadFile 读 GB 级文件进内存,runtime: out of memory 是秒级报错。关键不是“能不能分”,而是“不把整块拉进来”。
-
os.Open打开源文件后,用f.ReadAt(buf[:], offset)安全读指定位置——它线程安全,多 goroutine 并发读不同 offset 不用加锁 - 每次读前显式检查
n < len(buf),最后一片不足大小时只取buf[:n],否则会带脏数据 - 别用
bufio.Reader包裹后再调ReadAt——它不支持随机读,offset 会错乱 - 分片大小设
4 * 1024 * 1024(4MB):太小增加 I/O 次数;太大仍可能触发 GC 压力
分片哈希必须独立计算且持久化到元数据
单纯按字节切完再统一算整个文件 hash,只要原始文件改一个字节,所有后续分片 hash 全变——去重、断点续传、校验全失效。
- 每个分片写入磁盘后,立刻用
sha256.Sum256计算自身哈希:sum := sha256.Sum256{}; sum.Write(data); hash := sum.Sum(nil) - 存哈希时别用
fmt.Sprintf("%x", hash)转字符串——占空间、易编码歧义;直接存[32]byte值类型最稳 - 元数据建议用 map[string][32]byte 或本地 JSON 文件记录:
file_id → {chunk_0: [32]byte, chunk_1: [32]byte} - 若要支持断点续传,必须把已成功上传的分片哈希也落盘,否则重启后无法判断哪些已写入
RPC 分发必须显式注册 + gob 编码 + 导出约束
rpc: client protocol error 不是网络不通,是 net/rpc 在静默失败:默认只认 gob,且不校验方法签名,错一个字段名就卡住。
- 服务端注册必须用
rpc.RegisterName("ShardService", &ShardServer{}),且ShardServer方法首字母大写(导出)、参数/返回值类型也必须导出 - 客户端连接后,第一时间用
client.Call("ShardService.Upload", args, &reply)单测,别等业务堆完才验证 - RPC 方法里禁止传
os.File、chan、func——gob不支持,直接 panic - 调试加
log.SetFlags(log.Lshortfile),并在 handler 开头打日志,确认是否真进到了方法内部
HTTP 上传 handler 必须自己实现 multipart 解析与分片路由
http.FileServer 只处理 GET,你发 curl -F "file=@a.txt" 它直接回 405 Method Not Allowed。
立即学习“go语言免费学习笔记(深入)”;
- 上传接口必须自定义
http.HandlerFunc,用req.ParseMultipartForm(32 << 20)解析表单,或直接读 raw body(Content-Type: application/octet-stream) - 分片路由逻辑得自己写:按
file_id % N分发到节点,或接一致性哈希;没元数据服务(如 etcd/PostgreSQL)就别谈高可用 - 每个分片存为独立文件,命名含序号(如
chunk_001),便于合并时排序;不要用 UUID 当 chunk_id,否则服务端无法去重 - 合并前必须检查是否收齐
0..N-1所有分片,缺一片就 400;合并后立刻用sha256.New()+io.Copy校验最终文件 hash,不一致就删光重来
分片边界固定、哈希独立计算、RPC 类型导出、HTTP 不依赖 FileServer——这四点漏掉任一,上线后必在大文件或并发压测时暴露。尤其注意:io.ReadAt 返回的 n 和 io.EOF 组合判断,是最后一片截断是否正确的唯一依据。


















