Django项目关联文件批量下载报错:TypeError问题求助
下载项目关联所有文件的实现问题
我想在view.py里写个方法,实现下载某个项目关联的所有文件。现在用Python的ZipFile包来压缩文件,但代码一直写不对。
我的模型(Models)
class File(models.Model): project = models.ForeignKey(Project, on_delete=models.CASCADE) project_slug = AutoSlugField(populate_from='project', always_update=True) pub_date = models.DateTimeField(auto_now_add=True) def directory_path(instance, filename): return 'files_project/project_{0}/{1}'.format(instance.project_slug, filename) upload = models.FileField(upload_to=directory_path)
我的视图(View)
def DownloadAllFilebyProject(request, id): project = Project.objects.get(id=id) files = project.file_set.all() zf = zipfile.ZipFile(os.path.join(settings.MEDIA_ROOT, 'files_project/project_{0}/'.format(project.slug), 'allfile.zip'), 'w') for file in files: zf.writestr(os.path.join(settings.MEDIA_ROOT, 'files_project/project_{0}/'.format(project.slug)), file) zf.close() return HttpResponse(print(zf))
我的模板(Template)
<a href="{% url 'apppro:DownloadAllFilebyProject' project.id %}" class="btn btn-warning float-right" method="get"> descargar todos los archivos</a>
报错信息
TypeError at /downloadallzipfiles/13 object of type 'File' has no len()
问题分析
报错核心是zf.writestr()用法错误:这个方法第二个参数需要字符串或字节流,但你直接传入了File模型对象,Python尝试获取对象长度时触发错误。另外代码还有两个明显问题:
- 直接在服务器MEDIA目录生成zip文件,会残留大量临时文件占用存储空间;
- 返回
HttpResponse(print(zf))完全无效,没有返回正确的下载内容。
修正后的视图代码
from io import BytesIO import zipfile import os from django.http import HttpResponse from django.conf import settings from .models import Project def DownloadAllFilebyProject(request, id): project = Project.objects.get(id=id) files = project.file_set.all() # 在内存中创建zip文件,避免生成服务器临时文件 zip_buffer = BytesIO() # 使用ZIP_DEFLATED启用压缩,也可以用ZIP_STORED不压缩 with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf: for file_obj in files: # 获取文件在服务器上的实际路径 file_full_path = file_obj.upload.path # 设置zip内的文件名(这里用原文件名,也可以自定义路径) zip_inner_name = os.path.basename(file_full_path) # 将本地文件写入zip包 zf.write(file_full_path, zip_inner_name) # 将缓冲区指针移到起始位置,准备读取内容 zip_buffer.seek(0) # 配置响应头,让浏览器识别为下载文件 response = HttpResponse(zip_buffer, content_type='application/zip') response['Content-Disposition'] = f'attachment; filename="project_{project.slug}_all_files.zip"' return response
额外说明
- 使用
with语句管理ZipFile,无需手动调用close(),代码更安全简洁; - 用
BytesIO在内存中处理zip文件,避免服务器产生冗余文件; Content-Disposition头指定了下载文件名,用户浏览器会自动识别为下载操作;- 如果需要在zip内保留项目的目录结构,可以修改
zip_inner_name为相对路径,比如f"project_{project.slug}/{os.path.basename(file_full_path)}"。
内容的提问来源于stack exchange,提问作者Drennos
相关产品推荐
相关产品推荐

