Django下载PNG图片无法打开问题排查及代码修复方案
Django下载PNG文件损坏无法打开的修复方案
问题描述
开发Django应用时需要通过临时文件存储实现图片下载功能,应用部署在服务器端,本地Windows设备访问站点触发下载后,得到的PNG文件无法正常打开,Windows照片应用提示「似乎我们不支持此文件格式」。
原始问题代码(Views.py)
def download_png(request, study): Names(study) # 获取name变量的函数 Retrieve(study) # 获取data变量的函数 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) pngfilename = str(name) + "_" + str(current_time) + ".png" filepath = BASE_DIR + '/polls/graphics/' + pngfilename temp, filepath = tempfile.mkstemp(suffix='.png') with open(temp, 'w') as f: f = df2img.plot_dataframe(data) df2img.save_dataframe(fig=f, filename=filepath) response = HttpResponse(temp, content_type=mimetypes.guess_type(filepath)) response['Content-Disposition'] = "attachment; filename=%s" % pngfilename return response
故障原因
代码存在4处明显逻辑错误,直接导致返回的文件内容损坏、格式识别失败:
- 构造
HttpResponse时直接传入了mkstemp返回的整数型文件描述符,没有读取文件实际的二进制内容,客户端收到的根本不是PNG文件数据 - 操作文件时使用了文本写入模式
'w',二进制图片经过文本模式的编码转义会直接损坏内容 mimetypes.guess_type()返回值为(mime类型, 编码格式)的元组,直接传入content_type参数会导致客户端拿到错误的格式标识,无法识别文件类型- 文件路径逻辑混乱,前序定义的存储路径被
mkstemp的返回值覆盖,图片保存路径和响应读取路径完全不匹配
修复方案
Django针对文件下载场景提供了专门的FileResponse类,相比原生HttpResponse对二进制文件传输做了流式优化,内存占用更低、传输稳定性更高,实测可完美解决该问题,修复后代码如下:
def download_png(request, study): Names(study) Retrieve(study) pngfilename = str(name) + "_" + str(current_time) + ".png" mime_type, _ = mimetypes.guess_type(pngfilename) fig = df2img.plot_dataframe(data) df2img.save_dataframe(fig=fig, filename=pngfilename) response = FileResponse(open(pngfilename, 'rb'), content_type=mime_type) response['Content-Disposition'] = "attachment; filename=%s" % pngfilename os.remove(pngfilename) return response
修复逻辑说明
- 替换响应类为
FileResponse,打开文件时指定二进制读模式'rb',保证PNG二进制内容完整无转义传输 - 拆分
mimetypes.guess_type()返回的元组,仅传入正确的MIME类型值,让客户端可正常识别PNG格式 - 理顺文件读写路径,图片保存后直接读取返回,传输完成后调用
os.remove()删除临时文件,不会在服务器残留冗余文件 - 移除无意义的文件描述符错误操作,避免路径不匹配、内容读取错误问题
内容的提问来源于stack exchange,提问作者Hamza Jamal
相关产品推荐
相关产品推荐

