可行但需处理四大硬伤:路径分隔符须将windows反斜杠替换为zip规范的正斜杠,上传须流式避免内存溢出并设http超时,失败需重试与状态记录,gzip压缩后必须显式检查close()错误。

直接用 archive/zip 压缩 + http.Post 上传是可行的,但线上稳定运行必须处理路径兼容、临时文件安全、HTTP 超时和错误重试这四个硬伤。
压缩目录时路径分隔符不一致导致 zip 内文件名乱码
Windows 下 filepath.Walk 返回的路径含 ,而 ZIP 规范只认 /;若不做转换,Linux 服务器解压后文件名会变成 dirsubile.txt(实际创建为带反斜杠的单个文件名),而非预期目录结构。
- 必须在构造
header.Name前统一替换:strings.ReplaceAll(path, "\", "/") -
strings.TrimPrefix要用原始src_dir的标准化路径(推荐先filepath.Clean(src_dir)) - 若源路径含盘符(如
E:data),需额外去掉:\部分,否则header.Name以E:/开头,解压时可能被拒绝或写到根目录
上传大文件时 HTTP 连接超时或内存爆掉
用 os.ReadFile 读整个文件再传 multipart,100MB 文件就占 100MB 内存;且默认 http.DefaultClient 的超时是 0(无限等待),网络抖动时 goroutine 卡死。
- 改用流式上传:打开文件后,用
multipart.Writer边读边写入请求 body,避免全量加载 - 显式设置 client:
&http.Client{Timeout: 5 * time.Minute},尤其对慢速上传场景 - 上传前检查文件大小,超过阈值(如 500MB)直接跳过或报错,防止 OOM
自动上传任务中失败后无法恢复或重复上传
定时器触发上传,但压缩失败、网络中断或服务端返回 5xx,若没记录状态,下次仍会尝试压缩已传过的文件,造成冗余或冲突。
- 压缩完成立即生成校验文件(如
v1_zip.zip.sha256),上传成功后再删除;下次启动先扫描是否存在未上传的 .sha256 文件 - 不要依赖
os.RemoveAll(zip_file_name)清旧包——它可能删掉正在上传的文件;改用带时间戳的唯一文件名(v1_202606170804.zip) - 上传函数返回 error 后,应 sleep 一段随机时间(如 30–90s)再重试,避免雪崩式重连
gzip.Writer.Close() 被忽略导致压缩文件损坏
这是最隐蔽的坑:用 gzip.NewWriter 压缩日志再上传,若只 defer w.Close() 但没检查其返回值,一旦磁盘满或权限不足,w.Close() 报错但程序无感知,最终上传的是空或截断的 .gz 文件。
- 必须显式捕获
w.Close()的 error:if err := w.Close(); err != nil { return err } - 压缩完成后,用
os.Stat校验输出文件大小是否 > 0,再进入上传流程 - 别在
defer里关源文件(如file.Close())——io.Copy可能没读完就返回,导致部分数据丢失
真正难的不是写通逻辑,而是让压缩路径可移植、上传过程可中断可续传、错误能自愈。这些点不提前卡死,上线后只会出现在凌晨三点的告警里。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











