======================== ファイルアップロード ======================== :revision-up-to: 17812 (1.4) .. currentmodule:: django.core.files.uploadedfile 多くの Web サイトにとって、ファイルアップロードのサポートは不可欠ですね。 Django がアップロードされたファイルを扱うとき、ファイルデータは最終的に :attr:`request.FILES` に入ります (``request`` オブジェクトの詳細は :doc:`リクエスト/レスポンスオブジェクト ` のドキュメ ントを参照してください)。このドキュメントでは、ファイルがどのようにしてディ スクやメモリ上に保存されるかを説明し、そのデフォルトの動作をカスタマイズす る方法について説明します。 ファイルアップロードの基本 ============================ :class:`~django.forms.FileField` を含む以下のような簡単なフォームを考えて みましょう:: from django import forms class UploadFileForm(forms.Form): title = forms.CharField(max_length=50) file = forms.FileField() このフォームからの入力を扱うビューは、ファイルデータを ``request.FILES`` で受け取ります。 :attr:`request.FILES ` は ファイルデータの入った辞書で、辞書のキーはフォームクラス中の :class:`~django.forms.FileField` (または :class:`~django.forms.ImageField` や :class:`~django.forms.FileField` のサブクラス) の名前です。従って、 上の例では、 ``request.FILES['file']`` でファイルデータにアクセスできます。 もしリクエストメソッドが ``POST`` であり、尚且つ ``
`` において アトリビュート ``enctype="multipart/form-data"`` を含むリクエストが ポストされたのであれば、 :attr:`request.FILES ` はデータのみを含んでいる事に注意して下さい。 そうでない場合 ``request.FILES`` は空です。 ほとんどの場合は、「 :ref:`binding-uploaded-files` 」の節で説明した方法に従っ て、 ``request`` からデータを取り出してフォームに渡すだけでアップロードファ イルを処理できます:: from django.http import HttpResponseRedirect from django.shortcuts import render_to_response # アップロードファイルを処理する関数を import します from somewhere import handle_uploaded_file def upload_file(request): if request.method == 'POST': form = UploadFileForm(request.POST, request.FILES) if form.is_valid(): handle_uploaded_file(request.FILES['file']) return HttpResponseRedirect('/success/url/') else: form = UploadFileForm() return render_to_response('upload.html', {'form': form}) :attr:`request.FILES ` を明示的にフォーム のコンストラクタに渡さねばならないので注意してください。 これはフォームにファイルデータを結びつけるために必要な手順です。 アップロードファイルの処理 ---------------------------- .. class:: UploadedFile パズルの最後のピースは :attr:`request.FILES ` からの実際のデータファイルをハンドリングすることです。 この辞書の各エントリーはアップロードされたファイルをシンプルなラッパで 包んだ ``UploadedFile`` オブジェクトです。 通常、アップロードされた内容にアクセスする為にこれらのメソッドのいずれか を使う事になるでしょう: .. method:: read() アップロードされたデータ全体をファイルから読み出します。このメソッ ドを使うときに十分注意してください。というのも、アップロードされた ファイルが巨大だと、メモリに読み込む際にシステムの容量を越してしま うかもしれないからです。そのようなときは、後述の ``chunks()`` を使う とよいでしょう。 .. method:: multiple_chunks() ファイルが大きくて、複数のチャンクに分けて読み出すべきである場合に ``True`` を返します。デフォルトの設定では、 2.5 Mbytes より大きなファ イルに対して ``True`` を返します。サイズの閾値は設定でき、後で説明 します。 .. attribute:: chunks() ファイルのチャンクを返すジェネレータです。 ``multiple_chunks()`` が ``True`` の場合には、 ``read()`` ではなくこのメソッドを使ってくださ い。 実際には、常に ``chunks()`` を使うのがよいでしょう。後述の例を参照 してください。 .. attribute:: name ``my_file.txt`` のような、アップロードされたファイルの名前です。 .. attribute:: size アップロードファイルのサイズ(単位バイト)です。 ``UploadedFile`` オブジェクトは他にもいくつかメソッドや属性を備えています。 詳しくは `UploadedFile オブジェクト`_ を参照してください。 まとめると、アップロードファイルの一般的な処理例は以下の通りです:: def handle_uploaded_file(f): destination = open('some/file/name.txt', 'wb+') for chunk in f.chunks(): destination.write(chunk) destination.close() 上の例では、巨大なファイルを ``read()`` してシステムの容量を超えないように、 ``UploadedFile.chunks()`` を使っています。 アップロードされたファイルの保存先 ---------------------------------------- アップロードされたファイルデータは、処理され保存される前に、システムのどこ かに一時的に記憶されていなければなりません。 デフォルトの設定では、アップロードされたファイルデータが 2.5 Mbytes より小 さければ、 Django はファイルデータ全体をメモリに保持します。そのため、ファ イルデータの保存処理はメモリからディスクへの書き込みだけで実現され、高速で す。 しかし、ファイルデータが大きすぎる場合、 Django はファイルデータをシステム のテンポラリディレクトリに一時ファイルとして保存します。従って、 \*nix ライ クのプラットフォームでは、 Django は ``/tmp/tmpzfp6I6.upload`` のような ファイルを生成します。ファイルデータがとても大きければ、 Django がデータを ディスクにストリーム書き込みするにつれて、一時ファイルのサイズが増えてゆく のを観察できるでしょう。 2.5 Mbytes や ``/tmp`` といった仕様は、単に「妥当なデフォルト値」にすぎませ ん。アップロード時の挙動をカスタマイズしたり完全に置き換えたりしたければ、 この後の詳細説明に進んでください。 アップロードファイルハンドラの挙動を変更する ---------------------------------------------- ファイルアップロードの挙動は、以下の 3 つの設定で制御できます: :setting:`FILE_UPLOAD_MAX_MEMORY_SIZE` アップロードされたファイルをメモリに保存する上限のサイズです。 設定値よりも大きなファイルがアップロードされると、ファイルデータは ディスクに書き込まれます。 デフォルト値は 2.5 Mbytes です。 :setting:`FILE_UPLOAD_TEMP_DIR` :setting:`FILE_UPLOAD_MAX_MEMORY_SIZE` より大きなファイルがアップロー ドされたときにファイルデータを保存するディレクトリです。 デフォルト値はシステム標準の一時ファイルディレクトリです (UNIX ライクのシステムでは ``/tmp`` です)。 :setting:`FILE_UPLOAD_PERMISSIONS` アップロードされたファイルに設定するファイルモードで、数字で表現 (例: ``0644``) します。ファイルモードの意味は :func:`os.chmod` のドキュメントを参照してください。 この値を設定しないか、 ``None`` にすると、アップロードされたファイ ルのモードはオペレーティングシステムに依存します。ほとんどのプラッ トフォームでは、一時ファイルのファイルモードは ``0600`` で、メモリ からファイルにデータを書き出すときにはシステム標準の umask を使います。 .. warning:: ファイルモードにあまりくわしくないのなら、先頭の ``0`` がとても 重要だということに注意してください。先頭の 0 は、値が 8 進数で あることを示しています。 ``644`` のように指定すると、全くおかし な挙動になってしまうでしょう。 **ファイルモードの先頭には常に 0 をつけてください**. :setting:`FILE_UPLOAD_HANDLERS` アップロードファイルを処理する実際のハンドラです。 この設定を変更すると、ファイルアップロードの処理を完全にカスタマイ ズでき、 Django のアップロード処理全体の置き換えすらできます。 詳しくは後述の `アップロードハンドラ`_ を参照してください。 デフォルト値は以下の通りです:: ("django.core.files.uploadhandler.MemoryFileUploadHandler", "django.core.files.uploadhandler.TemporaryFileUploadHandler",) このデフォルトの設定では、アップロードされたファイルをまずメモリに 保存しようと試み、その後ファイルへの保存を試みます。 .. _documentation for os.chmod: http://docs.python.org/lib/os-file-dir.html .. _`os.chmod のドキュメント`: `documentation for os.chmod`_ ``UploadedFile`` オブジェクト =============================== :class:`File` から継承したものの他に、 ``UploadedFile`` オブジェクトは、以 下のメソッドや属性を定義しています。 .. attribute:: UploadedFile.content_type ``content-type`` ヘッダの内容 (:mimetype:`text/plain` や :mimetype:`application/pdf` など) です。ユーザが指定した他のデータ同様、 この値がアップロードされたファイルのコンテンツタイプを正確に表してい ると信用してはなりません。アップロードされたファイルのコンテンツタイプ が ``content-type`` ヘッダと一致しているか確かめておく必要があります。 「値は一応信用するが、検証し」てください。 .. attribute:: UploadedFile.charset ``content-type`` が :mimetype:`text/*` のときに、ブラウザが指定した 文字セット (``utf8`` など) です。「値は一応信用するが、検証する」よう にしてください。 .. attribute:: UploadedFile.temporary_file_path() アップロードファイルがディスクに一時保存されているときにのみ使える メソッドで、一時ファイルの完全なパスを返します。 .. note:: 通常の Python のファイルオブジェクトと同じく、アップロードファイルをイ テレータとして扱うと、ファイルの内容を一行づつ読めます: .. code-block:: python for line in uploadedfile: do_something_with(line) しかし、標準の Python ファイルとは *違って* 、 :class:`UploadedFile` は 改行文字として ``\n`` (いわゆる Unix形式) しか認識しません。 ``\n`` 以 外の改行で終端されたアップロードファイルを処理する必要があるのなら、ビュ ー内で適切に処理する必要があります。 アップロードハンドラ ===================== ユーザがファイルをアップロードすると、 Django はファイルデータを *アップロードハンドラ (upload handler)* と呼ばれるクラスに渡します。このク ラスはアップロードされたファイルデータを処理するためのクラスです。 デフォルトのアップロードハンドラは :setting:`FILE_UPLOAD_HANDLERS` で以下の ように定義されています:: ("django.core.files.uploadhandler.MemoryFileUploadHandler", "django.core.files.uploadhandler.TemporaryFileUploadHandler",) ``MemoryFileUploadHandler`` と ``TemporaryFileUploadHandler`` は、Django の デフォルトのファイルアップロード処理、すなわち、小さいファイルはメモリに読 み込み、大きいファイルはディスクに読み込むという動作を実現します。 カスタムのハンドラを書けば、ファイルの処理方法をカスタマイズできます。例え ば、カスタムハンドラによってユーザレベルで容量制限を課したり、データをオン ザフライで圧縮したり、プログレスバーを描画したり、受け取ったデータをローカ ルに保存せず、直接別のストレージに送ったりできます。 アップロードハンドラをオンザフライで変更する ---------------------------------------------- ビューごとに別々のアップロード処理を行いたい場合があります。その場合、 リクエストごとに ``request.upload_handlers`` を変更してアップロードハンドラ のチェインをオーバライドできます。デフォルトでは、 ``request.upload_handlers`` には :setting:`FILE_UPLOAD_HANDLERS` に指定した ハンドラのリストが入っていますが、これを他のリストに変更できます。 例えば、 ``ProgressBarUploadHandler`` というハンドラを書いて、 Ajax ウィジェットに何らかのアップロード状態のフィードバックを返させたいとしましょ う。以下のようにすれば、アップロードハンドラチェインに自作のハンドラを付加 できます:: request.upload_handlers.insert(0, ProgressBarUploadHandler()) この場合は、プログレスバーのハンドラを他のハンドラ *よりも前に* 実行したい ので、 (``append()`` ではなく) ``list.insert()`` を使うことになるでしょう。 アップロードハンドラチェインは、順番に実行されることを思い出してください。 完全にアップロードハンドラチェインを置き換えてしまいたければ、単にリストを 代入してください:: request.upload_handlers = [ProgressBarUploadHandler()] .. note:: アップロードハンドラチェインを変更できるのは、 ``request.POST`` や ``request.FILES`` にアクセスする *よりも前* だけです。アップロードの処 理が始まってしまった後でハンドラを変更しても何の意味もありません。 ``request.POST`` や ``request.FILES`` を読み出した後で ``request.upload_handlers`` を変更しようとすると、 Django はエラーを送 出します。 従って、アップロードハンドラチェインはビュー処理の出来るだけ早い段階で 変更しておくことになるでしょう。 また ``request.POST`` は通常与えられる :class:`~django.middleware.csrf.CsrfViewMiddleware` によって許可されます。 これはビューの中でアップロードハンドラの変更をする為に :func:`~django.views.decorators.csrf.csrf_exempt` を使う必要がある事を 意味します。 またそのとき :func:`~django.views.decorators.csrf.csrf_protect` を リクエストの実際のプロセスの関数上で使う必要があります。 このときハンドラがCSRFチェックが行われる前にファイルアップロードを受け取 り始める可能性がある事に注意して下さい。 Example code: .. code-block:: python from django.views.decorators.csrf import csrf_exempt, csrf_protect @csrf_exempt def upload_file_view(request): request.upload_handlers.insert(0, ProgressBarUploadHandler()) return _upload_file_view(request) @csrf_protect def _upload_file_view(request): ... # Process request カスタムのアップロードハンドラを書く --------------------------------------- ファイルアップロードハンドラは、全て ``django.core.files.uploadhandler.FileUploadHandler`` のサブクラスでなけれ ばなりません。アップロードハンドラはどこに記述してもかまいません。 必須のメソッド ~~~~~~~~~~~~~~~~ カスタムのファイルアップロードハンドラは以下のメソッドを **必ず** 定義せねばなりません: ``FileUploadHandler.receive_data_chunk(self, raw_data, start)`` ファイルアップロード時に、データの「チャンク」を受け取ったときに呼 び出されます。 ``raw_data`` はアップロードされたデータの入ったバイト文字列です。 ``start`` は ``raw_data`` チャンクのファイルデータ中の開始位置です。 このメソッドが返すデータは、次のアップロードハンドラの ``receive_data_chunk`` メソッドにフィードされます。この仕組みによって、 あるハンドラにデータを「フィルタ」させ、他のハンドラに入力できます。 ``receive_data_chunk`` で受け取ったデータを後続のハンドラに処理させたく ない場合には ``None`` を返してください。このメソッドは、アップロードさ れたデータを自分で処理して、他のハンドラにデータを保存させたくない場合に 便利です。 ``StopUpload`` や ``SkipFile`` といった例外を送出すると、アップロード 処理は中断し、アップロードファイルの処理を行いません。 ``FileUploadHandler.file_complete(self, file_size)`` ファイルのアップロードが終了したときに呼び出されます。 このハンドラは ``request.FILES`` に入れるための ``UploadedFile`` オブジ ェクトを返さねばなりません。後続のアップロードハンドラに ``UploadedFile`` を返させたい場合は ``None`` を返してください。 オプションのメソッド ~~~~~~~~~~~~~~~~~~~~~~ カスタムのアップロードハンドラでは、以下のオプションのメソッドや属性を定義 できます: ``FileUploadHandler.chunk_size`` Django がアップロードファイルをメモリ上に読み込み、ハンドラに渡すときに 使うチャンクのサイズです。バイト単位です。この属性は、 ``FileUploadHandler.receive_data_chunk`` で読み込まれるチャンクのサイズ 指定でもあります。 パフォーマンスを最大化するには、チャンクのサイズを ``4`` の倍数とし、 2GB (2\ :sup:`31` バイト) 以下とすべきです。複数のハンドラがそれぞれ別々 のチャンクサイズを提供していた場合、 Django は最小のチャンクサイズを使い ます。 デフォルト値は 64*2\ :sup:`10` バイトまたは 64 KB です。 ``FileUploadHandler.new_file(self, field_name, file_name, content_type, content_length, charset)`` 新たなファイルアップロードが開始されるときに呼び出されるコールバックです。 このメソッドは、アップロードハンドラがまだデータを受け取っていない段階で 呼び出されます。 ``field_name`` はファイルの ```` フィールドの名前です。 ``file_name`` は unicode のファイル名としてブラウザから提供された値です。 ``content_type`` は ``'image/jpeg'`` のような MIME タイプで、ブラウザから 提供された値です。 ``content_length`` はファイルの長さで、ブラウザから提供された値です。 ブラウザがファイルサイズを提供しないこともあり、その場合は ``None`` です。 ``charset`` は (``utf8`` のような) 文字セットで、ブラウザから提供さ れた値です。 ``content_length`` と同様、指定されないこともあります。 他のハンドラにファイルを処理させたくない場合、このメソッドから ``StopFutureHandlers`` 例外を送出してもかまいません。 ``FileUploadHandler.upload_complete(self)`` ファイル全体のアップロードが終了したときに呼び出されるコールバックです。 ``FileUploadHandler.handle_raw_input(self, input_data, META, content_length, boundary, encoding)`` このメソッドを使うと、生の HTTP 入力の解析を完全にオーバライドできます。 ``input_data`` は ``read()`` をサポートするファイルライクオブジェクトです。 ``META`` は ``request.META`` と同じです。 ``content_length`` は ``input_data`` に入っているデータの長さです。 ``input_data`` から ``content_length`` バイト以上を読み出そうとしてはなり ません。 ``boundary`` はリクエストの MIME バウンダリです。 ``encoding`` はリクエストのエンコーディングです。 アップロードの処理を他のハンドラに継続させたい場合には ``None`` を、 リクエストの直接処理に適したデータ構造を新たに生成して返したければ ``(POST, FILES)`` のタプルを返してください。