Почему при отправке ответа на создание FileResponse в Django возвращается ошибка разрешения?

В папке temp находится временный файл video.mp4, который должен быть создан, затем отправлен пользователю как вложение, а затем удален. Следующий код возвращает PermissionError: [WinError 32] для этой строки: os.remove(file_path). Почему?

Не удаляю ли я файл слишком рано?
def download_video(request):
    if request.method == 'POST':
        link_input_data = request.POST.get('link_input_field')  # todo change to use django forms later
        download_cda.start_download(link_input_data)
        file_path = "temp\\" + "video.mp4"
        response = None
        try:
            file = open(file_path, "rb")
            response = FileResponse(file, as_attachment=True, filename="video.mp4")
        except Exception as e:
            print(e)  # todo add exception code
        finally:
            os.remove(file_path)
        return response
    else:
        form = UrlForm()
        return render(request, 'index.html', {"url_form": form})

Я не смог найти ничего об этом в документации: [FileResponse] (https://docs.djangoproject.com/en/5.0/ref/request-response/#django.http.FileResponse)

Почему? Я слишком рано удаляю файл?

Да, потому что FileResponse [Django-doc] работает потоковым образом: он читает фрагмент файла, передает его клиенту, затем читает следующий фрагмент и т.д. Если вы возвращаете ответ, значит, файл еще не полностью прочитан.

Мы можем использовать список _resource_closers в ответе, чтобы удалить файл:

def download_video(request):
    if request.method == 'POST':
        link_input_data = request.POST.get(
            'link_input_field'
        )  # todo change to use django forms later
        download_cda.start_download(link_input_data)
        file_path = 'temp\\' + 'video.mp4'
        response = None
        def closer():
            return os.remove(file_path)
        try:
            response = FileResponse(
                open(file_path, 'rb'), as_attachment=True, filename='video.mp4'
            )
            response._resource_closers.append(closer)
            return response
        except Exception as e:
            print(e)  # todo add exception code
            closer()
    form = UrlForm()
    return render(request, 'index.html', {'url_form': form})

But serving files with Django is however often not a good idea. Usually what you do is let the webserver like Nginx or Apache serve the file. For Nginx with an X-Accel-Redirect header [nginx-doc].

В этом случае вы можете даже добавить post_action в nginx, чтобы удалить файл после того, как Nginx обслужил его.

Вернуться на верх