在Python中获取文件的大小4种办法
'# 在Python中获取文件的大小4种办法
一、背景与问题
在开发文件处理系统时,获取文件大小是常见需求。例如:
- 上传文件时限制大小
- 日志文件清理时判断是否超过阈值
- 文件传输时计算传输量
- 系统监控时统计磁盘使用情况
但不同场景下需要不同方法,本文将深入探讨四种实现方案,涵盖文件系统元数据、文件对象读取、压缩文件处理等场景。
二、基本原理
文件大小获取本质是读取文件系统元数据或实际读取文件内容。不同方法的差异主要体现在:
- 文件系统接口:通过os.path或stat模块访问文件系统元数据
- 文件读取方式:通过文件对象逐块读取内容
- 压缩文件处理:通过zipfile库解析压缩包
- 内存映射:通过mmap模块处理大文件
三、环境准备
# 安装依赖(如需处理压缩文件)
pip install python-magic四、核心实现
方法一:通过文件系统元数据获取(推荐)
import os
def get_file_size(path):
"""
通过文件系统接口获取文件大小
支持所有文件系统(包括网络文件系统)
"""
try:
return os.path.getsize(path)
except OSError as e:
print(f"获取文件大小失败: {e}")
return -1关键点解释:
os.path.getsize()直接读取文件系统元数据- 适用于所有文件系统(本地/网络/压缩包)
- 无需打开文件,效率最高(O(1)时间复杂度)
- 需处理文件路径不存在、权限不足等异常
方法二:通过文件对象读取(适用于大文件)
def get_file_size_by_read(path):
"""
通过文件对象逐块读取计算文件大小
适用于无法直接读取元数据的场景
"""
try:
with open(path, 'rb') as f:
size = 0
while True:
chunk = f.read(65536) # 每次读取64KB
if not chunk:
break
size += len(chunk)
return size
except IOError as e:
print(f"读取文件失败: {e}")
return -1关键点解释:
- 通过文件对象读取内容计算总长度
- 适用于无法直接读取元数据的特殊文件
- 分块读取避免内存溢出(适合大文件)
- 需注意文件关闭的异常处理
方法三:处理压缩文件(zip格式)
import zipfile
def get_compressed_file_size(path):
"""
获取压缩文件中所有文件的总大小
"""
try:
with zipfile.ZipFile(path, 'r') as zipf:
total_size = sum(file_info.file_size for file_info in zipf.infolist())
return total_size
except zipfile.BadZipFile as e:
print(f"压缩文件解析失败: {e}")
return -1关键点解释:
- 通过zipfile模块解析压缩包
- 直接读取压缩包中的文件信息
- 不需要解压文件即可获取大小
- 需处理压缩文件格式错误等异常
方法四:内存映射方式(适用于特殊文件系统)
import mmap
def get_file_size_by_mmap(path):
"""
通过内存映射获取文件大小
适用于特殊文件系统(如管道、内存文件)
"""
try:
with open(path, 'rb') as f:
with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
return mm.size()
except (OSError, ValueError) as e:
print(f"内存映射失败: {e}")
return -1关键点解释:
- 使用mmap模块创建内存映射
- 适用于特殊文件系统和内存文件
- 可以直接访问文件内容
- 需处理内存映射失败等异常
五、完整案例
文件上传校验系统
import os
import zipfile
import magic
class FileValidator:
def __init__(self, max_size=1024*1024*10): # 10MB
self.max_size = max_size
def validate_file(self, file_path):
"""验证文件大小和格式"""
try:
# 获取文件大小
size = self.get_file_size(file_path)
if size > self.max_size:
raise ValueError(f"文件过大: {size} bytes")
# 检查文件类型
mime = magic.Magic(mime=True)
file_type = mime.from_file(file_path)
# 检查压缩文件
if file_type.startswith('application/zip'):
self.validate_compressed_file(file_path)
return True
except Exception as e:
print(f"文件校验失败: {e}")
return False
def get_file_size(self, path):
"""统一获取文件大小的方法"""
# 优先尝试文件系统接口
try:
return os.path.getsize(path)
except Exception as e:
# 落回文件读取方式
return self.get_file_size_by_read(path)
def validate_compressed_file(self, path):
"""验证压缩文件内容"""
with zipfile.ZipFile(path, 'r') as zipf:
for file_info in zipf.infolist():
if file_info.filename == '..':
raise ValueError("包含危险路径")
if file_info.file_size > self.max_size:
raise ValueError(f"压缩文件内文件过大: {file_info.file_size} bytes")关键点说明:
- 统一的文件大小获取接口
- 支持多种文件类型
- 包含安全检查(防止路径遍历)
- 自动选择最合适的方法
六、源码解析
以get_file_size_by_read方法为例:
def get_file_size_by_read(path):
try:
with open(path, 'rb') as f:
size = 0
while True:
chunk = f.read(65536) # 每次读取64KB
if not chunk:
break
size += len(chunk)
return size
except IOError as e:
print(f"读取文件失败: {e}")
return -1逐行解释:
- 使用
with语句确保文件正确关闭 open以二进制模式读取文件- 64KB是常见分块大小,平衡内存和性能
- 循环读取直到文件末尾
- 累加每块的大小
- 捕获IO异常并返回错误码
七、进阶使用
多线程处理
from concurrent.futures import ThreadPoolExecutor
def get_files_size_in_parallel(paths):
"""多线程获取多个文件大小"""
with ThreadPoolExecutor() as executor:
results = list(executor.map(get_file_size, paths))
return results性能优化
- 对于大量小文件,使用
os.path.getsize更高效 - 对于大文件,使用
get_file_size_by_read避免内存占用 - 对于压缩文件,直接解析zipinfo更高效
八、性能与工程实践
性能对比
| 方法 | 时间复杂度 | 适用场景 | 内存占用 |
|---|---|---|---|
| os.path.getsize | O(1) | 常规文件 | 极低 |
| 文件读取 | O(n) | 特殊文件 | 低 |
| zip解析 | O(1) | 压缩文件 | 低 |
| mmap | O(1) | 特殊文件系统 | 高 |
异常处理
- 文件路径不存在:
FileNotFoundError - 权限不足:
PermissionError - 文件过大:
MemoryError(需处理内存溢出)
安全考虑
- 路径遍历漏洞:
../../etc/passwd等危险路径 - 压缩包炸弹:大压缩包导致内存溢出
- 非法文件格式:恶意文件可能导致解析错误
九、常见问题与踩坑
常见错误
错误示例:
with open('file.txt', 'r') as f: size = len(f.read())问题:读取大文件会占用大量内存
错误示例:
os.path.getsize('file.txt') # 本地文件 os.path.getsize('http://example.com/file.txt') # 网络文件问题:网络文件无法直接使用os.path.getsize
解决方案
- 使用
get_file_size_by_read处理大文件 使用
requests获取网络文件大小import requests def get_remote_file_size(url): response = requests.head(url) return int(response.headers.get('content-length', 0))
性能陷阱
- 对于大文件,
os.path.getsize比读取文件快100倍以上 - 压缩文件的
file_size是压缩后的大小,不是解压后的实际大小
十、最佳实践
推荐方案
| 场景 | 推荐方法 | 原因 |
|---|---|---|
| 本地文件 | os.path.getsize | 最高效 |
| 大文件 | get_file_size_by_read | 避免内存问题 |
| 压缩文件 | get_compressed_file_size | 直接解析 |
| 网络文件 | requests.head | 轻量级获取 |
编码规范
- 使用
with语句处理文件 - 优先使用文件系统接口
- 对敏感操作添加日志记录
- 对异常进行详细分类处理
十一、总结
获取文件大小是文件处理系统的基础功能,但不同场景需要不同方法:
- 常规文件推荐使用
os.path.getsize,它是最高效且最简单的实现 - 大文件应使用分块读取的方式避免内存溢出
- 压缩文件需要特殊处理,直接解析压缩包信息更高效
- 特殊文件系统需要使用内存映射技术
在实际开发中,建议:
- 对于常规文件处理,统一使用
get_file_size接口 - 对于敏感操作添加安全校验(如路径过滤)
- 对大文件处理添加性能监控
- 对网络文件处理使用轻量级获取方式
通过理解不同方法的原理和适用场景,可以更有效地处理文件大小相关的业务需求。
评论已关闭