Python shutil.copyfileobj 怎样控制大文件复制缓冲区
来源:17golang原创
时间:2026-10-23 08:56:16 206浏览 收藏
用 shutil.copyfileobj() 复制大文件时,真正控制内存占用的是第三个参数 length。它表示每一轮从源对象读取的缓冲区大小:省略时由 Python 分块复制,传入正数时使用指定大小,传入负数则可能一次把剩余内容读入内存。生产代码通常应优先使用明确的正数,并根据输入流的性质调整。
官方文档地址:https://docs.python.org/3/library/shutil.html#shutil.copyfileobj
如果目标是稳定复制大内容,先把 length 当作“单次读取上限”来设计,而不是把它当作文件总大小。默认分块适合不确定的输入,正数适合需要控制内存的任务,负数只适用于你明确接受一次性读取的短数据。
先分清 length 的真实含义
copyfileobj(fsrc, fdst, length) 面向的是两个 file-like object,不要求它们一定来自普通文件。文件、BytesIO、HTTP 响应流和临时文件都可以提供类似的读取或写入接口。
| 写法 | 内存语义 | 适用判断 |
|---|---|---|
| 省略 length | 按内部块分段读取,避免无界的一次性加载 | 输入规模不确定时的默认选择 |
length=1024 * 1024 | 每次最多读取约 1 MiB | 希望明确控制单次缓冲区 |
length=-1 | 不再按块循环,可能一次读取全部剩余内容 | 只有短小且内存可控的输入才考虑 |
官方文档还特别说明:如果源对象当前的位置不是 0,复制只会从当前位置到末尾。这个细节很容易让“目标文件少了一截”被误判成缓冲区问题。
用正数缓冲区复制大文件
对于本地文件,建议以二进制模式打开,并把缓冲区值写成有名字的常量。下面的示例只表达复制边界,不把某个数值宣称为所有磁盘和网络环境的最佳值。
from pathlib import Path
from shutil import copyfileobj
CHUNK_SIZE = 1024 * 1024 # 每轮最多读取 1 MiB,限制复制阶段的临时内存
source = Path("backup.tar")
target = Path("backup-copy.tar")
with source.open("rb") as src, target.open("wb") as dst:
copyfileobj(src, dst, length=CHUNK_SIZE) # 让源流和目标流保持二进制类型
dst.flush() # 后续若要立即读取目标文件,先刷新写入缓冲区
这里的关键不是“1 MiB 一定最快”,而是把单次读取的边界显式化。缓冲区太小会增加循环和系统调用次数,太大则会抬高单个任务的瞬时内存;多任务并发复制时,应把每个任务的缓冲区一起估算。

按流类型做取舍,而不是盲目放大缓冲区
在高并发任务中,旧的“每次读取全部剩余内容”做法会把输入大小直接转化为单任务内存压力。更稳妥的设计是把源流、缓冲区和目标流分开考虑:
- 本地大文件:使用固定正数,让内存占用与文件总大小脱钩。
- 网络输入:用中等块大小换取吞吐和延迟的平衡,不因为网络慢就扩大到整个响应。
- 临时文件或内存流:仍要关注源对象当前位置,目标对象若要马上读取则显式
flush()或关闭。
这是一种架构取舍:用更多循环次数换取可预测的内存曲线。真正的“上线结果”应由目标环境中的吞吐、并发数、磁盘和网络条件共同决定,不能只凭一个示例值下结论。若复制任务由队列并发执行,可先按“任务数 × 单任务缓冲区”估算上界,再决定是否需要更小的块或更低的并发。

四个容易误判的边界
源流不是从开头开始。如果上游已经读过请求头、文件头或一部分数据,copyfileobj() 会从当前位置继续复制。需要从开头复制时,应由调用方先明确 seek(0),而不是修改缓冲区参数。
文本流和二进制流不能混用。文件内容是字节时使用 rb 与 wb;若把二进制读取结果写入文本流,可能触发类型错误。编码转换应放在明确的文本处理层,不要交给复制函数猜测。
复制完成不等于目标已经刷新。copyfileobj() 不保证目标流在函数返回时已经 flush。复制到可读的临时文件或内存流后,先执行 flush() 或关闭,再进行后续读取。
不要把负数当成“更大的缓冲区”。负数改变的是复制策略:它取消按块循环,可能一次读取所有剩余数据。只有短数据、内存边界明确的场景才适合使用。
相关问题
为什么复制后的文件为空?先检查目标流是否仍处于写入状态,以及读取前是否执行了 flush();如果是内存流,还要确认读取位置是否需要重新定位。
length 应该设置多大?没有脱离环境的固定答案。先根据并发数和可用内存设上界,再用实际吞吐观察调整;大文件场景优先保证可预测,而不是追求一次调用完成。
copyfileobj 会复制文件权限和时间吗?不会。它只复制 file-like object 的内容;如果还需要文件元数据,应另行选择 shutil.copy()、copy2() 或专门的元数据处理逻辑。
-
151 收藏
-
379 收藏
-
336 收藏
-
149 收藏
-
209 收藏
-
407 收藏
-
314 收藏
-
372 收藏
-
369 收藏
-
407 收藏
-
116 收藏
-
486 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习