Python 3.12 无内置文件写入进度监控机制,需手动分块写入并累加字节数;tqdm + 自定义计数器是最可靠方案,避免依赖 tell()、stat() 或 fsync() 判断进度。

Python 3.12 本身不提供直接监控「文件写入进度」的内置机制——因为 open() 写入是流式、缓冲的,操作系统和 Python 运行时并不暴露实时已刷盘字节数。所谓“进度”,只能靠你自己控制写入节奏 + 主动计数。
为什么 shutil.copyfileobj() 的 length 参数不能直接当进度依据?
它只是预估总大小(比如传入 os.path.getsize(src)),但实际写入过程中无法得知当前已写入磁盘的准确字节数;尤其遇到网络文件系统、加密挂载或写缓存未 flush 时,tell() 在目标文件句柄上可能始终返回 0 或滞后严重。
-
shutil.copyfileobj()默认用 64KB 块读写,length只用于计算百分比起点,不触发实时同步 - 调用
dst_file.flush()不等于数据已落盘,需额外os.fsync(dst_file.fileno())(代价高,别每块都调) - 若源是
io.BytesIO或 HTTP 响应流,length可能为None或不准确
用 tqdm + 手动分块写入是最稳的实操方案
绕过不可靠的底层反馈,自己掌控每次写多少、累计多少——这是唯一能给出可信进度条的方式。关键点:不要依赖目标文件句柄的位置,而是用你自己的计数器。
from tqdm import tqdm
import time
<p>def copy_with_progress(src_path, dst_path, chunk_size=8192):
size = os.path.getsize(src_path)
with open(src_path, "rb") as src, open(dst_path, "wb") as dst:</p><h1>注意:tqdm 的 total 是预估总字节数,不是块数</h1><pre class="brush:php;toolbar:false;"> with tqdm(total=size, unit="B", unit_scale=True, desc="Copying") as pbar:
while (chunk := src.read(chunk_size)):
dst.write(chunk)
pbar.update(len(chunk)) # ✅ 用实际写出的字节数更新
# 可选:每写完一块后 sleep(0) 让 tqdm 刷新更及时(尤其小文件)
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
立即学习“Python免费学习笔记(深入)”;
- 必须用
pbar.update(len(chunk)),而非pbar.update(1)(那是按块数,不是字节数) -
chunk_size太小(如 1)会导致 tqdm 刷新频繁、I/O 拖慢;太大(如 1MB)会让进度条“卡顿”。8KB–64KB 是平衡点 - 如果源是网络流(如
requests.Response.raw),需确保其Content-Length可信,否则total设为None,进度条变成“计时器”模式
监控“真实落盘”进度需要 OS 级手段,但通常没必要
Linux 下可用 /proc/PID/fdinfo/FD 查看内核 write offset,Windows 需用 GetFileInformationByHandleEx(FileNameInfo) —— 但这些值反映的是内核缓冲区偏移,仍不等于磁盘物理位置,且 Python 无跨平台封装。
- 普通日志写入、配置保存等场景,
flush() + fsync()后再更新进度条即可,无需强求“物理落盘”精度 - 若真需落盘确认(如金融级日志),应改用原子写法:
write to tmp → os.fsync() → os.replace(),进度条只覆盖写 tmp 阶段 - 试图轮询
os.stat(dst).st_size来监控?在高并发或 NFS 上极不可靠,且性能差
进度条的可信度,永远取决于你控制写入的粒度,而不是事后去猜系统干了什么。别把 os.fsync 当进度信号,它只是安全栅栏。

















