-
Django to AWS S3 업로드간 발생하는 I/O operation on closed file 해결방법Django 2021. 6. 21. 05:04
서론
필자는 Django rest api(DRF)와 React를 연결하여 500 에러가 난 증상에서 시작되었습니다.
필자의 해결 진행 순서대로 진행되므로 양해 바랍니다.
I/O operation on closed file에러
Django와 AWS의 S3를 연동 후 파일을 업로드하게 된다면 아래와 같은 에러 메시지를 받을 수 있다.
File "C:\Users\user\Desktop\project\venv\Desktop\lib\site-packages\storages\backends\s3boto3.py", line 447, in _save content.seek(0, os.SEEK_SET) ValueError: I/O operation on closed file.
이러한 증상들을 찾다보니 한 블로그에서 증상이 비슷한 글을 찾게 되었는데
그 블로그에서는 form딴에서 save를 할 때의 에러였다.
AWS S3에는 이미지가 이미 업로드된 상태가 되고, 메모리에 파일이 상주하게 됩니다.
그럼 추후 작업후 commit=True일 때 save가 들어오게 되면
S3에서는 이미 닫힌 파일이기 때문에 저장할 때 에러가 발생하게 되죠.필자와 동일한 증상이면서도 DRF가 아닌 form을 사용하기에 해결방법이 달랐다.
그러나 어떠한 이유에서 일어난 것 인지 알기 때문에 조금 더 찾아봤다.
그래서 찾은 방법으로는 2가지가 있었는데 들어가기 앞서 설명을 해야 할 부분이 있다.
위의 에러명대로 s3boto3.py에서 실패한 것이므로 s3boto3.py에 가서 코드를 추가하거나
또는 코드 파일을 프로젝트에 직접 생성하면 된다. (이름은 상관없이 코드만 적용되면 된다.)
그리고 그 추가한 내용을 settings.py 아래와 같이 새로운 코드를 쓴다는 설정을 적용해야 작동이 된다.
s3boto3.py에 설정 시
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.CustomS3Boto3Storage'
Custom 파일 생성 시
필자는 CustomS3Boto3Storage.py 에 CustomS3Boto3Storage 클래스를 만들어줬다.
DEFAULT_FILE_STORAGE = 'CustomS3Boto3Storage.CustomS3Boto3Storage'
먼저 1번부터 보자면 필자는 실패했다.
동일하게 content.seek 부분에서 에러가 발생했다.(또는 parameters 에러)
그러나 많은 사람들이 이 코드로 정상적으로 작동했다는 댓글들이 많아 혹시 몰라 첨부한다.
class CustomS3Boto3Storage(S3Boto3Storage): def _save_content(self, obj, content, parameters): content.seek(0, os.SEEK_SET) content_autoclose = SpooledTemporaryFile() content_autoclose.write(content.read()) super(CustomS3Boto3Storage, self)._save_content(obj, content_autoclose, parameters) if not content_autoclose.closed: content_autoclose.close()
2번째 방법으로는 성공했는데
원래 적혀있던 주석을 대충 해석하자면
boto3은 업로드 시 파일을 닫는데 백엔드는 여전히 열려있을 것으로 예상되므로
임시파일을 만들어 그 파일을 읽은 후, 다시 삭제해주는 일을 한다.
class CustomS3Boto3Storage(S3Boto3Storage): def _save(self, name, content): content.seek(0, os.SEEK_SET) with SpooledTemporaryFile() as content_autoclose: content_autoclose.write(content.read()) return super(CustomS3Boto3Storage, self)._save(name, content_autoclose)
마치며
위의 코드뿐만 아니라 settings.py에도 boto3 storage 설정을 해줘야 한다는 점을 유의해야 한다.
Reference
https://gmyankee.tistory.com/281
https://stackoverflow.com/questions/61228944/i-o-operation-on-closed-file-in-django-with-imagekit
https://github.com/matthewwithanm/django-imagekit/issues/391#issuecomment-504678524
https://github.com/matthewwithanm/django-imagekit/issues/391#issuecomment-275367006
'Django' 카테고리의 다른 글
django.core.exceptions.SuspiciousFileOperation: Detected path traversal attempt (0) 2021.08.02 Django queryset distinct and sort (0) 2021.08.02 Django OneToOneField로 연결된 model 자동 생성 (0) 2021.06.14 Django Filefield의 file에서 name 가져오기 (0) 2021.06.14 Django 배포에 사용되는 WSGI란 무엇일까? (0) 2021.06.04