19

私はRESTスタイルのインターフェースを実装しており、HTTP PUTリクエストを介して(アップロードを介して)ファイルを作成できるようにしたいと考えています。TemporaryUploadedFileaまたはaのいずれかを作成し、InMemoryUploadedFileそれを既存のオブジェクトFileField.save()モデルの一部であるオブジェクトに渡して、ファイルを保存したいと思います。

ファイルのアップロード部分の処理方法がよくわかりません。request.FILES具体的には、これはプットリクエストであり、リクエストに存在しないため、アクセスできませんPUT

だから、いくつかの質問:

  • HttpRequestクラスの既存の機能、特にファイルのアップロードを処理する部分を活用できますか?ダイレクトはマルチパートMIMEリクエストではないことを知っているPUTので、そうは思いませんが、尋ねる価値があります。
  • 送信されているもののmimeタイプを推測するにはどうすればよいですか?私が正しく理解していれば、PUT本体はプレリュードのない単なるファイルです。したがって、ユーザーがヘッダーでmimeタイプを指定する必要がありますか?
  • これを大量のデータに拡張するにはどうすればよいですか?それは非常に非効率的であるため、私はそれをすべてメモリに読み込みたくありません。理想的には、私は何TemporaryUploadFileをし、関連するコードを実行しますか?一度に部分的に記述しますか?

Djangoをだましてリクエストとして処理させるこのコードサンプルを見てみました。私がそれを正しく理解していれば、フォームでエンコードされたデータのみを処理します。これはRESTであるため、フォームでエンコードされたデータが存在すると想定しないことが最善の解決策になります。ただし、何らかの形でmime(マルチパートではない)の使用に関する適切なアドバイスを聞いてうれしいです(ただし、アップロードには1つのファイルのみを含める必要があります)。PUTPOST

Django1.3は許容範囲です。request.raw_post_dataだから私はまたはrequest.read()(あるいは他のより良いアクセス方法)で何かをすることができます。何か案は?

4

3 に答える 3

8

Django1.3は許容範囲です。したがって、request.raw_post_dataまたはrequest.read()(または他のより良いアクセス方法)を使用して何かを行うことができます。何か案は?

触れたくはありませんrequest.raw_post_data。これは、リクエストの本文全体をメモリに読み込むことを意味します。これは、ファイルのアップロードについて話している場合、非常に大量になる可能性があるため、これrequest.read()が方法です。これはDjango<=1.2でも実行できますがHttpRequest、プライベートインターフェイスを使用する正しい方法を見つけるために掘り下げてみる必要があります。これは、コードがDjango>=1.3と互換性があることを確認するための実際のドラッグです。 。

クラスの既存のファイルアップロード動作の部分MultiPartParserを複製することをお勧めします。

  1. からアップロードハンダーを取得request.upload_handlersします(デフォルトではMemoryFileUploadHandler&になりますTemporaryFileUploadHandler
  2. HttpRequestリクエストのコンテンツの長さを決定します(またはでContent-Lengthを検索MultiPartParserして、これを行う正しい方法を確認します)。
  3. アップロードされたファイルのファイル名を決定するには、URLの最後のパス部分を使用してクライアントに指定させるかContent-Dispositionヘッダーの「filename=」部分に指定させます。
  4. ハンドラーごとhandler.new_fileに、関連する引数を使用して呼び出します(フィールド名のモックアップ)
  5. request.read()各チャンクを使用して呼び出し、チャンクでリクエスト本文を読み取りhandler.receive_data_chunk()ます。
  6. ハンドラー呼び出しごとhandler.file_complete()に、値が返される場合は、アップロードされたファイルです。

送信されているもののmimeタイプを推測するにはどうすればよいですか?私が正しく理解していれば、PUT本体はプレリュードのない単なるファイルです。したがって、ユーザーがヘッダーでmimeタイプを指定する必要がありますか?

クライアントにContent-Typeヘッダーで指定させるか、Pythonのmimetypeモジュールを使用してメディアタイプを推測します。

私はあなたがこれをどのようにやっていくのかを知りたいと思います-それは私が自分自身を調べることを意味していたことです、それがどうなるかを私に知らせてくれるコメントがあれば素晴らしいです!


要求に応じて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、存在しない文字セットを許可するのかについて、2つの考えを持っています。いずれにせよ、私はContent-Typeクライアント側の責任として設定を有効に置いています。

同様に、私のプロトコルでは、ファイル名が渡されます。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!
于 2011-04-23T01:19:30.510 に答える
1

新しい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()データを読み取り専用にする場合は、への呼び出しを削除できます。

于 2017-02-28T15:50:13.887 に答える
0

Django 2.2で作業しているときにこの問題が発生し、PUTリクエストを介してファイルをアップロードするために機能するものを探していました。

from django.http import QueryDict
from django.http.multipartparser import MultiValueDict
from django.core.files.uploadhandler import (
    SkipFile,
    StopFutureHandlers,
    StopUpload,
)


class PutUploadMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        method = request.META.get("REQUEST_METHOD", "").upper()
        if method == "PUT":
            self.handle_PUT(request)
        return self.get_response(request)

    def handle_PUT(self, request):
        content_type = str(request.META.get("CONTENT_TYPE", ""))
        content_length = int(request.META.get("CONTENT_LENGTH", 0))
        file_name = request.path.split("/")[-1:][0]
        field_name = file_name
        content_type_extra = None

        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 = ""

        upload_handlers = request.upload_handlers

        for handler in upload_handlers:
            result = handler.handle_raw_input(
                request.body,
                request.META,
                content_length,
                boundary=None,
                encoding=None,
            )
        counters = [0] * len(upload_handlers)
        for handler in upload_handlers:
            try:
                handler.new_file(
                    field_name,
                    file_name,
                    content_type,
                    content_length,
                    charset,
                    content_type_extra,
                )
            except StopFutureHandlers:
                break

        for chunk in request:
            for i, handler in enumerate(upload_handlers):
                chunk_length = len(chunk)
                chunk = handler.receive_data_chunk(chunk, counters[i])
                counters[i] += chunk_length
                if chunk is None:
                    # Don't continue if the chunk received by
                    # the handler is None.
                    break

        for i, handler in enumerate(upload_handlers):
            file_obj = handler.file_complete(counters[i])
            if file_obj:
                # If it returns a file object, then set the files dict.
                request.FILES.appendlist(file_name, file_obj)
                break
        any(handler.upload_complete() for handler in upload_handlers)
于 2020-05-16T15:29:24.357 に答える