使用smart-open读取S3文件结合FileResponse下载报错的解决咨询
解决S3文件通过smart-open与Django FileResponse结合的下载问题
问题说明
要实现网页端下载存储在S3桶中的文件(支持.txt、.pdf、.doc/.docx),使用smart-open访问S3资源时,结合Django的FileResponse出现报错:TypeError: expected str, bytes or os.PathLike object, not Reader。原代码尝试对smart-open返回的Reader对象再次调用open(),导致类型不匹配。
原错误代码:
@api_view(['GET']) def download_attachment(request, pk): session = boto3.Session(aws_access_key_id=AWS_ACCESS_KEY_ID, aws_secret_access_key=AWS_SECRET_ACCESS_KEY) obj = Attachment.objects.get(id=pk) with smart_opener(f's3://______media/public/{str(obj)}',"rb",transport_params={'client':session.client('s3')}) as attachment: response = FileResponse(open(attachment, 'rb')) # 错误:对已打开的Reader调用open() response['Content-Disposition'] = f'attachment; filename={obj.file.name}' return response
解决方案
smart-open的with上下文管理器已经返回了一个可读取的文件对象(Reader),直接将其传入FileResponse即可,无需再次调用open()。同时建议添加正确的Content-Type头,让浏览器能识别文件类型:
import mimetypes from django.http import FileResponse from rest_framework.decorators import api_view import boto3 from smart_open import open as smart_opener @api_view(['GET']) def download_attachment(request, pk): session = boto3.Session(aws_access_key_id=AWS_ACCESS_KEY_ID, aws_secret_access_key=AWS_SECRET_ACCESS_KEY) obj = Attachment.objects.get(id=pk) s3_path = f's3://______media/public/{str(obj)}' # 获取文件MIME类型,用于设置Content-Type mime_type, _ = mimetypes.guess_type(obj.file.name) mime_type = mime_type or 'application/octet-stream' # 兜底类型 with smart_opener(s3_path, "rb", transport_params={'client': session.client('s3')}) as attachment: response = FileResponse(attachment) # 设置下载头和类型 response['Content-Disposition'] = f'attachment; filename="{obj.file.name}"' response['Content-Type'] = mime_type return response
关键修改点
- 移除错误的
open(attachment, 'rb'),直接传递smart-open返回的文件对象给FileResponse - 新增
Content-Type头,通过mimetypes模块自动识别文件类型,提升浏览器处理兼容性 - 给文件名添加双引号,避免文件名包含空格或特殊字符时出现解析问题
内容的提问来源于stack exchange,提问作者user17817249
相关产品推荐
相关产品推荐

