Django测试中用openpyxl读取导出文件时出现BadZipFile错误
解决xlwt生成"xlsx"文件导致openpyxl读取BadZipFile错误的问题
问题根源
xlwt是专门生成**旧版Excel格式(.xls)**的库,完全不支持生成基于zip压缩的.xlsx格式。你导出时把文件名后缀设为.xlsx,但文件实际内容仍是二进制的xls格式,而openpyxl仅能读取标准的zip压缩型xlsx文件,这就是触发BadZipFile错误的核心原因。
手动下载后用LibreOffice另存可正常读取,是因为LibreOffice自动识别了文件的真实格式,并将其转换为标准xlsx格式。
解决方案
方案1:替换xlwt为支持xlsx的库(推荐)
使用openpyxl或xlsxwriter直接生成标准xlsx文件,这两个库都支持设置筛选功能,生成的文件能被openpyxl正常读取,单元测试也能顺利进行。
示例代码(xlsxwriter + Django):
from django.http import HttpResponse import xlsxwriter def export_filtered_excel(request): # 设置对应xlsx格式的响应头 response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') response['Content-Disposition'] = 'attachment; filename="filtered_data.xlsx"' # 内存中创建工作簿 workbook = xlsxwriter.Workbook(response, {'in_memory': True}) worksheet = workbook.add_worksheet() # 写入表头 worksheet.write('A1', '用户名') worksheet.write('B1', '部门') worksheet.write('C1', '入职日期') # 设置表头行的筛选范围 worksheet.autofilter('A1:C1') # 写入数据示例 data = [ ['张三', '技术部', '2023-01-01'], ['李四', '市场部', '2023-03-15'] ] for row_idx, row_data in enumerate(data, start=2): worksheet.write_row(row_idx, 0, row_data) # 关闭工作簿,自动将内容写入response workbook.close() return response
单元测试代码(openpyxl读取):
from django.test import TestCase from io import BytesIO from openpyxl import load_workbook class ExcelExportTest(TestCase): def test_export_and_read(self): response = self.client.get('/your-export-api/') self.assertEqual(response.status_code, 200) # 用BytesIO包装响应内容,读取xlsx文件 workbook = load_workbook(BytesIO(response.content)) sheet = workbook.active # 验证表头和数据 self.assertEqual(sheet['A1'].value, '用户名') self.assertEqual(sheet['A2'].value, '张三')
方案2:保留xlwt,修正格式匹配
如果必须继续使用xlwt,需要调整格式匹配:
- 将文件名后缀改为
.xls - 修改响应的
Content-Type为application/vnd.ms-excel - 单元测试时使用支持xls格式的库(如
xlrd)读取,而非openpyxl
修正后的导出代码:
from django.http import HttpResponse import xlwt def export_excel_xls(request): response = HttpResponse(content_type='application/vnd.ms-excel') response['Content-Disposition'] = 'attachment; filename="filtered_data.xls"' workbook = xlwt.Workbook(encoding='utf-8') worksheet = workbook.add_sheet('数据') # 写入表头和数据 worksheet.write(0, 0, '用户名') worksheet.write(0, 1, '部门') worksheet.write(1, 0, '张三') worksheet.write(1, 1, '技术部') # 注意:xlwt对xls格式的筛选支持有限,无法像xlsx那样直接设置autofilter,若需完整筛选功能,建议优先用方案1 workbook.save(response) return response
单元测试代码(xlrd读取):
from django.test import TestCase import xlrd class XlsExportTest(TestCase): def test_export_xls(self): response = self.client.get('/your-xls-export-api/') self.assertEqual(response.status_code, 200) workbook = xlrd.open_workbook(file_contents=response.content) sheet = workbook.sheet_by_index(0) self.assertEqual(sheet.cell_value(0, 0), '用户名')
总结
核心问题是格式伪装导致不匹配:xlwt生成的xls文件被改成xlsx后缀,让openpyxl误以为是zip压缩格式而报错。优先推荐方案1,换用支持xlsx的库,既能完美实现筛选功能,也能让单元测试流程顺畅。
内容的提问来源于stack exchange,提问作者nitroglycerin
相关产品推荐
相关产品推荐

