Как обрабатывать загрузку файлов через запрос PUT в Django?

Я реализую интерфейс REST-стиля и хотел бы иметь возможность создавать (через загрузку) файлы через HTTP-запрос PUT. Я хотел бы создать либо TemporaryUploadedFile либо InMemoryUploadedFile который затем могу передать в мой существующий FileField и FileField .save() для объекта, который является частью модели, тем самым сохраняя файл.

Я не совсем уверен, как обращаться с частью загрузки файла. В частности, это запрос put, у меня нет доступа к request.FILES поскольку он не существует в запросе PUT .

Итак, некоторые вопросы:

  • Могу ли я использовать существующую функциональность в классе HttpRequest , в частности, часть, которая обрабатывает загрузку файлов? Я знаю, что прямой PUT не является многопрофильным MIME-запросом, поэтому я так не думаю, но его стоит спросить.
  • Как я могу вывести тип mime того, что отправляется? Если у меня все получится, тело PUT будет просто файлом без прелюдии. Должен ли я поэтому требовать, чтобы пользователь указывал тип mime в своих заголовках?
  • Как это расширить до больших объемов данных? Я не хочу читать все это в память, так как это очень неэффективно. В идеале я бы сделал то, что делает TemporaryUploadFile и связанный с ним код, – записывайте его за раз за раз?

Я взглянул на этот пример кода, который заставляет Django обрабатывать PUT в качестве запроса POST . Если у меня все получится, это будет обрабатывать только кодированные данные. Это REST, поэтому лучшим решением было бы не предполагать, что форма закодированные данные будут существовать. Тем не менее, я рад услышать соответствующие рекомендации по использованию mime (а не multipart) каким-то образом (но загрузка должна содержать только один файл).

Django 1.3 является приемлемым. Поэтому я могу либо что-то сделать с request.raw_post_data или request.read() (или, как альтернатива, другим лучшим способом доступа). Есть идеи?

2 Solutions collect form web for “Как обрабатывать загрузку файлов через запрос PUT в Django?”

Django 1.3 является приемлемым. Поэтому я могу либо что-то сделать с request.raw_post_data или request.read () (или, как альтернатива, другим лучшим способом доступа). Есть идеи?

Вы не хотите касаться request.raw_post_data – это означает, что чтение всего тела запроса в память, что, если вы говорите о загрузке файлов, может быть очень большой, поэтому request.read() – это путь. Вы можете сделать это с помощью Django <= 1.2, но это означает, что нужно искать в HttpRequest чтобы выяснить правильный способ использования частных интерфейсов, и это реальное перетаскивание, чтобы затем обеспечить, чтобы ваш код также был совместим с Django> = 1.3.

Я бы предположил, что вы хотите сделать репликацию существующих частей поведения загрузки файлов в классе MultiPartParser :

  1. MemoryFileUploadHandler из request.upload_handlers (который по умолчанию будет MemoryFileUploadHandler & TemporaryFileUploadHandler )
  2. Определите длину содержимого запроса (поиск Content-Length в HttpRequest или MultiPartParser чтобы увидеть правильный способ сделать это.)
  3. Определите имя файла загруженного файла, либо указав его клиентом с помощью последней части пути URL-адреса, либо указав его клиенту в разделе «filename =» заголовка Content-Disposition .
  4. Для каждого обработчика вызовите handler.new_file с соответствующими аргументами (издеваясь над именем поля)
  5. Прочитайте тело запроса в кусках с помощью request.read() и handler.receive_data_chunk() для каждого фрагмента.
  6. Для каждого обработчика вызова handler.file_complete() , и если он возвращает значение, это загруженный файл.

Как я могу вывести тип mime того, что отправляется? Если у меня все получится, тело PUT будет просто файлом без прелюдии. Должен ли я поэтому требовать, чтобы пользователь указывал тип mime в своих заголовках?

Позвольте клиенту указать его в заголовке Content-Type или использовать модуль mimetype для python, чтобы угадать тип носителя.

Мне было бы интересно узнать, как вы справляетесь с этим – это то, что я имел в виду, чтобы смотреть в себя, быть здоровым, если бы вы могли прокомментировать, чтобы сообщить мне, как это происходит!


Редактировать по Ninefingers по запросу, это то, что я сделал, и полностью основан на вышеизложенном и источнике django.

 upload_handlers = request.upload_handlers content_type = str(request.META.get('CONTENT_TYPE', "")) content_length = int(request.META.get('CONTENT_LENGTH', 0)) if content_type == "": return HttpResponse(status=400) if content_length == 0: # both returned 0 return HttpResponse(status=400) content_type = content_type.split(";")[0].strip() try: charset = content_type.split(";")[1].strip() except IndexError: charset = "" # we can get the file name via the path, we don't actually file_name = path.split("/")[-1:][0] field_name = file_name 

Поскольку я определяю API здесь, поддержка кросс-браузера не вызывает беспокойства. Что касается моего протокола, то не предоставление правильной информации является сломанным запросом. Я в двух умах о том, хочу ли я сказать image/jpeg; charset=binary image/jpeg; charset=binary или если я разрешу несуществующие кодировки. В любом случае, я устанавливаю параметр Content-Type действительным образом как ответственность на стороне клиента.

Аналогично, для моего протокола имя файла передается. Я не уверен, для чего field_name параметр field_name и источник не дал много подсказок.

То, что происходит ниже, на самом деле намного проще, чем кажется. Вы спросите каждого обработчика, будет ли он обрабатывать исходный ввод. Как автор вышеупомянутых состояний, у вас есть MemoryFileUploadHandler & TemporaryFileUploadHandler по умолчанию. Ну, оказывается, MemoryFileUploadHandler будет, когда его попросят создать new_file решить, будет ли он обрабатывать файл или не обрабатывать его (на основе различных настроек). Если он решит, что это произойдет, он выдает исключение, иначе он не будет создавать файл и разрешит другому обработчику.

Я не уверен, какова цель counters , но я сохранил ее из источника. Остальное должно быть простым.

 counters = [0]*len(upload_handlers) for handler in upload_handlers: result = handler.handle_raw_input("",request.META,content_length,"","") for handler in upload_handlers: try: handler.new_file(field_name, file_name, content_type, content_length, charset) except StopFutureHandlers: break for i, handler in enumerate(upload_handlers): while True: chunk = request.read(handler.chunk_size) if chunk: handler.receive_data_chunk(chunk, counters[i]) counters[i] += len(chunk) else: # no chunk break for i, handler in enumerate(upload_handlers): file_obj = handler.file_complete(counters[i]) if not file_obj: # some indication this didn't work? return HttpResponse(status=500) else: # handle file obj! 

Более новые версии Django позволяют обрабатывать это намного проще благодаря https://gist.github.com/g00fy-/1161423

Я изменил данное решение следующим образом:

 if request.content_type.startswith('multipart'): put, files = request.parse_file_upload(request.META, request) request.FILES.update(files) request.PUT = put.dict() else: request.PUT = QueryDict(request.body).dict() 

для доступа к файлам и другим данным, например, в POST. Вы можете удалить вызовы в .dict() если вы хотите, чтобы ваши данные были доступны только для чтения.

  • Как загрузить IMAGE в качестве запуска приложения в QC с помощью API HP ALM REST
  • Добавление поведения REST в класс с флягой, для чертежей?
  • Django rest framework_classes метода ViewSet
  • Django Rest Framework добавляет метод уровня класса к api
  • Загрузка файла Django REST Framework, вызвавшая ошибку «Неподдерживаемый тип медиафайла« multipart / form-data »
  • Ищете совет по защите частного REST API, написанного в python-колбе
  • Как получить значение JSON из Post в приложении сервера web.py?
  • Добавление REST в Django
  • Python - лучший язык программирования в мире.