ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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.pyCustomS3Boto3Storage 클래스를 만들어줬다.

    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

    댓글

Designed by Tistory.