Django上传图片时Pillow报AttributeError: 'NoneType'无seek属性
问题:Django DRF FileUploadParser 处理图片时出现 AttributeError: 'NoneType' object has no attribute 'seek'
我尝试使用Django及Django REST Framework的FileUploadParser创建图片上传视图,实现上传图片并同时存储原图与缩略图的功能,但提交文件时出现错误。
代码实现
class ImageUploadParser(FileUploadParser): media_type = "image/*" class ImageUploadView(APIView): permission_classes = (permissions.IsAuthenticated,) parser_classes = [ImageUploadParser] def post(self, request, filename): if "file" not in request.data: raise ParseError("Empty content") file_obj = request.data["file"] try: img = Image.open(file_obj) img.verify() except UnidentifiedImageError: raise ParseError("Unsupported image type") path = settings.MEDIA_ROOT / f"{request.user.id}" unique = uuid.uuid4().hex extension = img.format.lower() filename_full = f"{unique}.{extension}" filename_thumb = f"{unique}.thumb.{extension}" if not os.path.exists(path): os.makedirs(path) img.thumbnail((580, 580), Image.ANTIALIAS) img.save(path / filename_thumb, "image/png", optimize=True) return Response(f"{request.user.id}/{filename_full}", status=status.HTTP_201_CREATED)
报错信息
Internal Server Error: /api/uploads/upload/test.png Traceback (most recent call last): File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/PIL/ImageFile.py", line 182, in load seek = self.load_seek File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/PIL/Image.py", line 529, in __getattr__ raise AttributeError(name) AttributeError: load_seek During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/asgiref/sync.py", line 486, in thread_handler raise exc_info[1] File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/django/core/handlers/exception.py", line 43, in inner response = await get_response(request) File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/django/core/handlers/base.py", line 253, in _get_response_async response = await wrapped_callback( File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/asgiref/sync.py", line 448, in __call__ ret = await asyncio.wait_for(future, timeout=None) File "/opt/homebrew/Cellar/python@3.10/3.10.9/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/tasks.py", line 408, in wait_for return await fut File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/asgiref/current_thread_executor.py", line 22, in run result = self.fn(*self.args, **self.kwargs) File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/asgiref/sync.py", line 490, in thread_handler return func(*args, **kwargs) File "/opt/homebrew/Cellar/python@3.10/3.10.9/Frameworks/Python.framework/Versions/3.10/lib/python3.10/contextlib.py", line 79, in inner return func(*args, **kwds) File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/django/views/decorators/csrf.py", line 55, in wrapped_view return view_func(*args, **kwargs) File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/django/views/generic/base.py", line 103, in view return self.dispatch(request, *args, **kwargs) File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/rest_framework/views.py", line 509, in dispatch response = self.handle_exception(exc) File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/rest_framework/views.py", line 469, in handle_exception self.raise_uncaught_exception(exc) File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception raise exc File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/rest_framework/views.py", line 506, in dispatch response = handler(request, *args, **kwargs) File "/Users/kevin/Workspace/cn-django/criticalnotes/uploads/views.py", line 74, in post img.thumbnail((580, 580), Image.ANTIALIAS) File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/PIL/Image.py", line 2618, in thumbnail im = self.resize(size, resample, box=box, reducing_gap=reducing_gap) File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/PIL/Image.py", line 2156, in resize self.load() File "/Users/kevin/Workspace/cn-django/.venv/lib/python3.10/site-packages/PIL/ImageFile.py", line 185, in load seek = self.fp.seek AttributeError: 'NoneType' object has no attribute 'seek'
问题原因
调用img.verify()后,PIL会将文件指针移动到文件末尾,并且关闭了对原始文件对象的引用,导致img对象内部的fp属性变为None。后续调用img.thumbnail()时,PIL需要重新读取文件内容,但无法访问已失效的文件指针,从而抛出错误。
解决方法
方法1:重置文件指针并重新加载图片
在verify()之后,将文件指针重置到开头,然后重新打开图片:
try: img = Image.open(file_obj) img.verify() # 重置文件指针到文件开头 file_obj.seek(0) # 重新加载图片 img = Image.open(file_obj) except UnidentifiedImageError: raise ParseError("Unsupported image type")
方法2:将文件内容读入内存(更可靠)
先把文件内容读取到内存中的BytesIO对象,避免文件指针移动的问题:
from io import BytesIO # ... file_obj = request.data["file"] # 读取文件内容到内存 file_content = file_obj.read() try: img = Image.open(BytesIO(file_content)) img.verify() # 重新用内存中的内容创建图片对象 img = Image.open(BytesIO(file_content)) except UnidentifiedImageError: raise ParseError("Unsupported image type")
补充:保存原图
你的代码中只实现了缩略图的保存,建议添加原图的保存逻辑:
# 保存原图 img_full = Image.open(BytesIO(file_content)) # 对应方法2的实现 img_full.save(path / filename_full, extension.upper(), optimize=True)
内容的提问来源于stack exchange,提问作者Kevin Renskers
相关产品推荐
相关产品推荐

