Django: Нить, вызывающая преждевременное закрытие InMemoryUploadedFile?

Добрый день,

У меня есть проблема, при которой CSV-файл, отправленный в View и затем переданный в новый поток для обработки, иногда закрывается преждевременно, и я не могу понять, почему. Такое поведение является периодическим и начало происходить только после того, как я перешел на использование нового потока для обработки файла.

Это первоначальный способ, которым я обрабатывал файл, и он работал, но для больших файлов это вызывало проблемы с таймаутом на клиенте:

class LoadCSVFile(APIView):
    permission_class = (IsAuthenticated,)
    parser_classes = [FormParser, MultiPartParser]

    def post(self, request):
        file = request.FILES['file']
        data_set = file.read().decode('utf-8')
        io_string = io.StringIO(data_set)

        for row_data in csv.reader(io_string, delimiter=',', quotechar='"'):
            print('row_data:', row_data)

        return Response({ 'message': 'File received and is currently processing.', 'status_code': status.HTTP_200_OK }, status.HTTP_200_OK)

Теперь я обрабатываю файл в новом потоке следующим образом:

class LoadCSVFile(APIView):
    permission_class = (IsAuthenticated,)
    parser_classes = [FormParser, MultiPartParser]

    def post(self, request):
        request_handler = RequestHandler(request)
        csv_handler = CSVHandler(request.FILES['file'])

        # Fire and forget the file processing.
        t = threading.Thread(target=request_handler.resolve, kwargs={ 'csv_handler': csv_handler })
        t.start()
        return Response({ 'message': 'File received and is currently processing.', 'status_code': status.HTTP_200_OK }, status.HTTP_200_OK)


class RequestHandler(object):
    def __init__(self, request : Request):
        self.request = request

    def resolve(self, **kwargs):
        csv_handler = kwargs['csv_handler']

        try:
            print('Processing file...')
            csv_handler.process_file()
        except Exception as e:
            print('process_file level error:', e)


class CSVHandler(object):
    def __init__(self, file):
        self.file = file

    def get_reader(self):
        # Error is raised at the following line: "I/O operation on closed file."
        data_set = self.file.read().decode('utf-8')
        io_string = io.StringIO(data_set)
        return csv.reader(io_string, delimiter=',', quotechar='"')

    def process_file(self, **kwargs):
        for row_data in self.get_reader():
            print('row_data:', row_data)

Какое-то время все было отлично, но потом я начал замечать периодические ошибки ввода-вывода.

  • Это происходит с большими (5000 строк) и маленькими (2 строки) файлами.
  • Я могу сделать 50 загрузок без появления ошибки, затем это произойдет 2 или 3 раза подряд. Или где-то между ними.
  • Как запрос сохраняется в RequestHandler, так и файл сохраняется в CSVHandler до инициирования потока, и я не знаю, как еще сохранить InMemoryUploadedFile живым, пока он мне не понадобится (csv_handler.get_reader()).

Есть предложения?

Спасибо за ваше время.

Проблема была вызвана тем, что основной поток возвращался из POST-запроса до того, как CSV-файл был открыт csv_handler.get_reader(). Я все еще не уверен, как файл теряется, пока RequestHandler и CSVHandler имеют ссылки на объекты request и file. Возможно, это особенность Django.

Я исправил это, переместив логику чтения в конструктор CSVHandler и используя блокировку для предотвращения состояния гонки.

class CSVHandler(object):
    def __init__(self, file):
        self.lock = threading.Lock()
        with self.lock:
            self.file = file
            data_set = self.file.read().decode('utf-8')
            io_string = io.StringIO(data_set)
            self.reader = csv.reader(io_string, delimiter=',', quotechar='"')

    def process_file(self, **kwargs):
        for row_data in self.reader:
            print('row_data:', row_data)
Вернуться на верх