Django FileResponse: PermissionError: [WinError 32] Процесс не может получить доступ к файлу, потому что он используется другим процессом

Я пытался вернуть файл с помощью django FileResponse, но столкнулся с проблемой: экземпляр файла не может быть удален после отправки ответа. До этой реализации я использовал модуль tempfile, но он также не работал.

from rest_framework import views, status
from django.http import FileResponse
import os


class DownloadShoppingCartView(views.APIView):
    def get(self, request):
        try:
            with open('test.txt', 'w+') as file:
                file.write('supertest\n')

            return FileResponse(
                open(file.name, mode='rb'),
                as_attachment=True,
                status=status.HTTP_200_OK
            )
        except Exception as e:
            print('There will be exception handling')
        finally:
            """I also have been trying to put here time.sleep(5),
 but there was no positive result.
            """
            os.remove(file.name)

Полное отслеживание:

Internal Server Error: /api/recipes/download_shopping_cart/
Traceback (most recent call last):
  File "C:\Users\Justm\dev\foodgram-project-react\backend\.venv\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "C:\Users\Justm\dev\foodgram-project-react\backend\.venv\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\Justm\dev\foodgram-project-react\backend\.venv\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "C:\Users\Justm\dev\foodgram-project-react\backend\.venv\lib\site-packages\django\views\generic\base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Users\Justm\dev\foodgram-project-react\backend\.venv\lib\site-packages\rest_framework\views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "C:\Users\Justm\dev\foodgram-project-react\backend\.venv\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "C:\Users\Justm\dev\foodgram-project-react\backend\.venv\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
    raise exc
  File "C:\Users\Justm\dev\foodgram-project-react\backend\.venv\lib\site-packages\rest_framework\views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "C:\Users\Justm\dev\foodgram-project-react\backend\recipes\views.py", line 141, in get
    os.remove(file.name)
PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'test.txt'

Пункт finally выполняется перед пунктом return.

Поскольку вы open(file.name, mode='rb'), который будет закрыт только после return и отправки ответа, os.remove в пункте finally не может получить доступ к файлу.

Вы можете реализовать прокси для удаления файлов после закрытия.

import os

from django.core.files.utils import FileProxyMixin


class RemoveAfterCloseFileProxy(FileProxyMixin):
    def __init__(self, file):
        self.file = file
        self.name = file.name

    def close(self):
        if not self.closed:
            self.file.close()
            try:
                os.remove(self.name)
            except FileNotFoundError:
                pass

    def __del__(self):
        self.close()

Использование:

class DownloadShoppingCartView(views.APIView):
    def get(self, request):
        try:
            with open('test.txt', 'w+') as file:
                file.write('supertest\n')

            return FileResponse(
                # open(file.name, mode='rb'),                           # Change this
                RemoveAfterCloseFileProxy(open(file.name, mode='rb')),  # to this
                as_attachment=True,
                status=status.HTTP_200_OK
            )
        except Exception as e:
            print('There will be exception handling')
        finally:
            # os.remove(file.name)    # Wrap this
            try:                      # in try-except
                os.remove(file.name)  #
            except PermissionError:   #
                pass                  #
Вернуться на верх