.. _topics-file-uploads: ======================== ファイルアップロード ======================== :revision-up-to: 8961 (1.0) .. currentmodule:: django.core.files .. versionadded:: 1.0 多くの Web サイトにとって、ファイルアップロードのサポートは不可欠ですね。 Django がアップロードされたファイルを扱うとき、ファイルデータは最終的に ``request.FILES`` に入ります (``request`` オブジェクトの詳細は :ref:`リクエスト/レスポンスオブジェクト ` のドキュメ ントを参照してください)。このドキュメントでは、ファイルがどのようにしてディ スクやメモリ上に保存されるかを説明し、そのデフォルトの動作をカスタマイズす る方法について説明します。 .. _Basic file uploads: ファイルアップロードの基本 ============================ ``FileField`` を含む以下のような簡単なフォームを考えてみましょう:: from django import forms class UploadFileForm(forms.Form): title = forms.CharField(max_length=50) file = forms.FileField() このフォームからの入力を扱うビューは、ファイルデータを ``request.FILES`` で 受け取ります。 ``request.FILES`` はファイルデータの入った辞書で、辞書のキー はフォームクラス中の ``FileField`` (または ``ImageField`` や ``FileField`` のサブクラス) の名前です。従って、上の例では、 ``request.FILES['file']`` で ファイルデータにアクセスできます。 ほとんどの場合は、「 :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}) ``request.FILES`` を明示的にフォームのコンストラクタに渡さねばならないので 注意してください。これはフォームにファイルデータを結びつけるために必要な手 順です。 .. _Handling uploaded files: アップロードファイルの処理 ---------------------------- ファイルアップロードというパズルの最後のピースは、 ``request.FILES`` の ファイルデータを実際に処理する部分です。 ``request.FILES`` の各エントリは ``UploadedFile`` オブジェクトです。 ``UploadedFile`` は単純なアップロードファ イルデータのラッパです。このインスタンスには、以下のようなメソッドがあり、 アップロードされたデータにアクセスできます: ``UploadedFile.read()`` アップロードされたデータ全体をファイルから読み出します。このメソッ ドを使うときに十分注意してください。というのも、アップロードされた ファイルが巨大だと、メモリに読み込む際にシステムの容量を越してしま うかもしれないからです。そのようなときは、後述の ``chunks()`` を使う とよいでしょう。 ``UploadedFile.multiple_chunks()`` ファイルが大きくて、複数のチャンクに分けて読み出すべきである場合に ``True`` を返します。デフォルトの設定では、 2.5 Mbytes より大きなファ イルに対して ``True`` を返します。サイズの閾値は設定でき、後で説明 します。 ``UploadedFile.chunk()`` ファイルのチャンクを返すジェネレータです。 ``multiple_chunks()`` が ``True`` の場合には、 ``read()`` ではなくこのメソッドを使ってくださ い。 実際には、常に ``chunks()`` を使うのがよいでしょう。後述の例を参照 してください。 ``UploadedFile.name`` ``my_file.txt`` のような、アップロードされたファイルの名前です。 ``UploadedFile.size`` アップロードファイルのサイズ(単位バイト)です。 ``UploadedFile`` オブジェクトは他にもいくつかメソッドや属性を備えています。 詳しくは `UploadedFile オブジェクト`_ を参照してください。 まとめると、アップロードファイルの一般的な処理例は以下の通りです:: def handle_uploaded_file(f): destination = open('some/file/name.txt', 'wb+') for chunk in f.chunks(): destination.write(chunk) 上の例では、巨大なファイルを ``read()`` してシステムの容量を超えないように、 ``UploadedFile.chunks()`` を使っています。 .. _Where uploaded data is stored: アップロードされたファイルの保存先 ---------------------------------------- アップロードされたファイルデータは、処理され保存される前に、システムのどこ かに一時的に記憶されていなければなりません。 デフォルトの設定では、アップロードされたファイルデータが 2.5 Mbytes より小 さければ、 Django はファイルデータ全体をメモリに保持します。そのため、ファ イルデータの保存処理はメモリからディスクへの書き込みだけで実現され、高速で す。 しかし、ファイルデータが大きすぎる場合、 Django はファイルデータをシステム のテンポラリディレクトリに一時ファイルとして保存します。従って、 \*nix ライ クのプラットフォームでは、 Django は ``/tmp/tmpzfp6I6.upload`` のような ファイルを生成します。ファイルデータがとても大きければ、 Django がデータを ディスクにストリーム書き込みするにつれて、一時ファイルのサイズが増えてゆく のを観察できるでしょう。 2.5 Mbytes や ``/tmp`` といった仕様は、単に「妥当なデフォルト値」にすぎませ ん。アップロード時の挙動をカスタマイズしたり完全に置き換えたりしたければ、 この後の詳細説明に進んでください。 .. _Changing upload handler behavior: アップロードファイルハンドラの挙動を変更する ---------------------------------------------- ファイルアップロードの挙動は、以下の 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``) します。ファイルモードの意味は `os.chmod のドキュメント`_ を参照してください。 .. _documentation for os.chmod: http://docs.python.org/lib/os-file-dir.html .. _`os.chmod のドキュメント`: `documentation for 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",) このデフォルトの設定では、アップロードされたファイルをまずメモリに 保存しようと試み、その後ファイルへの保存を試みます。 .. _UploadedFile objects: ``UploadedFile`` オブジェクト ================================= .. class:: UploadedFile :class:`File` から継承したものの他に、 ``UploadedFile`` オブジェクトは、以 下のメソッドや属性を定義しています。 ``UploadedFile.content_type`` ``content-type`` ヘッダの内容 (``text/plain`` や ``application/pdf`` など) です。ユーザが指定した他のデータ同様、こ の値がアップロードされたファイルのコンテンツタイプを正確に表してい ると信用してはなりません。アップロードされたファイルのコンテンツタ イプが ``content-type`` ヘッダと一致しているか確かめておく必要があ ります。「値は一応信用するが、検証し」てください。 ``UploadedFile.charset`` ``content-type`` が ``text/*`` のときに、ブラウザが指定した文字セッ ト (``utf8`` など) です。「値は一応信用するが、検証する」ようにして ください。 ``UploadedFile.temporary_file_path()`` アップロードファイルがディスクに一時保存されているときにのみ使える メソッドで、一時ファイルの完全なパスを返します。 .. note:: 通常の Python のファイルオブジェクトと同じく、アップロードファイルをイ テレータとして扱うと、ファイルの内容を一行づつ読めます: .. code-block:: python for line in uploadedfile: do_something_with(line) しかし、標準の Python ファイルとは *違って* 、 :class:`UploadedFile` は 改行文字として ``\n`` (いわゆる Unix形式) しか認識しません。 ``\n`` 以 外の改行で終端されたアップロードファイルを処理する必要があるのなら、ビュー 内で適切に処理する必要があります。 .. _Upload Handlers: アップロードハンドラ ===================== ユーザがファイルをアップロードすると、 Django はファイルデータを *アップロードハンドラ (upload handler)* と呼ばれるクラスに渡します。このク ラスはアップロードされたファイルデータを処理するためのクラスです。 デフォルトのアップロードハンドラは :setting:`FILE_UPLOAD_HANDLERS` で以下のように 定義されています:: ("django.core.files.uploadhandler.MemoryFileUploadHandler", "django.core.files.uploadhandler.TemporaryFileUploadHandler",) ``MemoryFileUploadHandler`` と ``TemporaryFileUploadHandler`` は、Django の デフォルトのファイルアップロード処理、すなわち、小さいファイルはメモリに読 み込み、大きいファイルはディスクに読み込むという動作を実現します。 カスタムのハンドラを書けば、ファイルの処理方法をカスタマイズできます。例え ば、カスタムハンドラによってユーザレベルで容量制限を課したり、データをオン ザフライで圧縮したり、プログレスバーを描画したり、受け取ったデータをローカ ルに保存せず、直接別のストレージに送ったりできます。 .. _Modifying upload handlers on the fly: アップロードハンドラをオンザフライで変更する ---------------------------------------------- ビューごとに別々のアップロード処理を行いたい場合があります。その場合、 リクエストごとに ``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 はエラーを送 出します。 従って、アップロードハンドラチェインはビュー処理の出来るだけ早い段階で 変更しておくことになるでしょう。 .. _Writing custom upload handlers: カスタムのアップロードハンドラを書く --------------------------------------- ファイルアップロードハンドラは、全て ``django.core.files.uploadhandler.FileUploadHandler`` のサブクラスでなけれ ばなりません。アップロードハンドラはどこに記述してもかまいません。 .. _Required methods: 必須のメソッド ~~~~~~~~~~~~~~~~ カスタムのファイルアップロードハンドラは以下のメソッドを **必ず** 定義せねばなりません: ``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`` を返してください。 .. _Optional methods: オプションのメソッド ~~~~~~~~~~~~~~~~~~~~~~ カスタムのアップロードハンドラでは、以下のオプションのメソッドや属性を定義 できます: ``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)`` のタプルを返してください。