必须用'wb'模式写入二进制数据,否则触发编码错误或文件损坏;需确保数据为bytes类型,大文件应分块写入,流式下载须用iter_content()或shutil.copyfileobj(),并注意路径、权限与目录存在性。

用 open() 以 'wb' 模式写入最直接
二进制数据(比如从网络请求、加密解密、图像处理得到的 bytes 对象)不能用文本模式保存,否则会触发编码错误或损坏内容。必须用 'wb'(write binary)模式打开文件,再调用 .write()。
常见错误现象:UnicodeEncodeError 或文件打开后乱码/无法解析(如图片打不开、PDF 损坏)——基本都是误用了 'w' 或没指定编码。
-
open('output.bin', 'wb')是唯一安全起点;'w'、'wt'、'wb+'(除非真要读写同文件)都容易出问题 - 数据源是
bytes类型才能直接.write();如果是bytearray或memoryview,也支持,但str必须先.encode() - 写入大文件时,别一次性
.write(data),改用分块写入避免内存峰值,例如每次.write(chunk)(chunk长度建议 8192 或 65536)
从 requests 响应流保存文件要绕过内存加载
如果二进制数据来自 HTTP 响应(比如下载大文件),用 response.content 会把整个响应体载入内存,可能 OOM。应该用 response.iter_content() 流式写入。
使用场景:下载视频、模型权重、数据库导出文件等 >100MB 的资源。
- 务必设置
stream=True初始化请求:requests.get(url, stream=True) - 检查状态码再写入,避免把 404/500 响应体当有效文件保存:
if resp.status_code == 200: - 示例关键片段:
with open('file.zip', 'wb') as f: for chunk in resp.iter_content(chunk_size=8192): if chunk: # 过滤 keep-alive 空块 f.write(chunk)
shutil.copyfileobj() 适合已有的二进制文件对象流转
当你手头已经是可读的二进制文件对象(比如 io.BytesIO、临时文件句柄、socket 接收流),且不想自己写循环,shutil.copyfileobj() 是更简洁、经过充分测试的选择。
参数差异:copyfileobj(fsrc, fdst, length=65536) 默认缓冲区 64KB,比手动 read()/write() 更少出错,也自动处理末尾不整除情况。
- 源对象必须有
.read()方法,目标对象必须有.write()方法 - 不要对同一个文件对象重复调用 —— 比如
copyfileobj(f, f)会导致不可预测行为 - 注意目标文件需提前以
'wb'打开,且未关闭;copyfileobj()不负责打开/关闭任何文件
路径和权限问题常被忽略
保存失败不一定是代码逻辑错,很可能是运行时没有目标目录写入权限,或路径含非法字符(Windows 下 : " / \ | ? *)、相对路径解析错位。
性能影响:在 NFS 或 Windows 网络驱动器上频繁小块写入会显著拖慢速度,此时应合并为较大块(如 1MB)再写。
- 用
os.path.dirname(filepath)+os.makedirs(..., exist_ok=True)确保父目录存在 - 路径尽量用
pathlib.Path构造,避免拼接错误:Path('data') / 'image.png' - Linux/macOS 下注意当前用户是否对目标目录有
w权限;Docker 容器中尤其容易因挂载方式导致只读
写二进制文件本身很简单,但数据来源、大小、路径环境、权限这四点任意一个没兜住,就会卡在“明明写了却打不开”这种问题上。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











