Django中解压multipart/form-data上传的gzip数据报错求助
问题诱因
- 内存流指针未复位:你向
compressedFile写入上传的gzip内容后,流指针停在末尾位置,后续gzip.GzipFile从指针位置开始读取,无法拿到任何压缩数据,导致解压结果为空,磁盘生成的文件大小为0。 - 文件句柄提前关闭:你在
with代码块内打开本地文件生成DjangoFile对象,with块执行结束后会自动关闭绑定的文件句柄,后续DjangoFileField处理逻辑调用seek(0)读取文件内容时,就会抛出seek of closed file错误。 - shutil版本额外问题:你已经提前调用
file.read()将上传文件的指针读到了末尾,后续直接把file传入gzip.GzipFile时,同样无法读取到压缩数据,最终也会生成空文件。 - 附加问题:你的curl命令额外加了
Content-Encoding: gzip请求头,该头是用于标识整个请求体为gzip压缩格式,会导致Django提前对整个请求做解压,你拿到的file对象实际已经是解压后的csv文件,自然无法再次用gzip逻辑处理。
修复方案
优先推荐内存解压方案,无需写入磁盘,性能更高,完全适配Django File对象的使用逻辑:
import io import gzip from django.core.files import File def create(self, validated_data): file: File = validated_data.get("file") ext = file.name.split(".")[-1].lower() if ext == "gz": # 读取上传的gzip文件原始内容 compressed_data = file.read() # 内存中完成解压 with gzip.GzipFile(fileobj=io.BytesIO(compressed_data), mode='rb') as decompressed_f: decompressed_data = decompressed_f.read() # 生成Django可直接使用的File对象,指定文件名适配后续逻辑 decompressed_fname = file.name[:-3] file = File(io.BytesIO(decompressed_data), name=decompressed_fname) ext = decompressed_fname.split(".")[-1].lower() # 原有后续业务逻辑 ...
配套测试curl命令移除多余请求头即可:
curl http://0.0.0.0:8000/upload/ -X 'POST' -F "input_type=data" -F "file=@data.csv.gz"
如果必须写入磁盘再生成File对象,只需调整文件打开逻辑,避免with块自动关闭文件句柄即可,代码如下:
if ext == "gz": compressed_data = file.read() decompressed_fname = file.name[:-3] # 解压写入磁盘 with gzip.GzipFile(fileobj=io.BytesIO(compressed_data), mode='rb') as decompressed_f: with open(decompressed_fname, "wb") as outfile: outfile.write(decompressed_f.read()) # 单独打开文件生成File对象,不使用with块自动关闭 outfile = open(decompressed_fname, "rb") file = File(outfile, name=decompressed_fname) ext = decompressed_fname.split(".")[-1].lower()
注意该方案需要在后续业务逻辑执行完成后手动调用outfile.close()释放文件句柄,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Dennis
相关产品推荐
相关产品推荐

