revision-up-to: | 8961 (1.0) |
---|
多くの Web サイトにとって、ファイルアップロードのサポートは不可欠ですね。 Django がアップロードされたファイルを扱うとき、ファイルデータは最終的に request.FILES に入ります (request オブジェクトの詳細は リクエスト/レスポンスオブジェクト のドキュメ ントを参照してください)。このドキュメントでは、ファイルがどのようにしてディ スクやメモリ上に保存されるかを説明し、そのデフォルトの動作をカスタマイズす る方法について説明します。
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'] で ファイルデータにアクセスできます。
ほとんどの場合は、「 アップロードされたファイルをフォームに結びつける 」の節で説明した方法に従っ て、 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 を明示的にフォームのコンストラクタに渡さねばならないので 注意してください。これはフォームにファイルデータを結びつけるために必要な手 順です。
ファイルアップロードというパズルの最後のピースは、 request.FILES の ファイルデータを実際に処理する部分です。 request.FILES の各エントリは UploadedFile オブジェクトです。 UploadedFile は単純なアップロードファ イルデータのラッパです。このインスタンスには、以下のようなメソッドがあり、 アップロードされたデータにアクセスできます:
ファイルのチャンクを返すジェネレータです。 multiple_chunks() が True の場合には、 read() ではなくこのメソッドを使ってくださ い。
実際には、常に chunks() を使うのがよいでしょう。後述の例を参照 してください。
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() を使っています。
アップロードされたファイルデータは、処理され保存される前に、システムのどこ かに一時的に記憶されていなければなりません。
デフォルトの設定では、アップロードされたファイルデータが 2.5 Mbytes より小 さければ、 Django はファイルデータ全体をメモリに保持します。そのため、ファ イルデータの保存処理はメモリからディスクへの書き込みだけで実現され、高速で す。
しかし、ファイルデータが大きすぎる場合、 Django はファイルデータをシステム のテンポラリディレクトリに一時ファイルとして保存します。従って、 *nix ライ クのプラットフォームでは、 Django は /tmp/tmpzfp6I6.upload のような ファイルを生成します。ファイルデータがとても大きければ、 Django がデータを ディスクにストリーム書き込みするにつれて、一時ファイルのサイズが増えてゆく のを観察できるでしょう。
2.5 Mbytes や /tmp といった仕様は、単に「妥当なデフォルト値」にすぎませ ん。アップロード時の挙動をカスタマイズしたり完全に置き換えたりしたければ、 この後の詳細説明に進んでください。
ファイルアップロードの挙動は、以下の 3 つの設定で制御できます:
アップロードされたファイルをメモリに保存する上限のサイズです。 設定値よりも大きなファイルがアップロードされると、ファイルデータは ディスクに書き込まれます。
デフォルト値は 2.5 Mbytes です。
FILE_UPLOAD_MAX_MEMORY_SIZE より大きなファイルがアップロー ドされたときにファイルデータを保存するディレクトリです。
デフォルト値はシステム標準の一時ファイルディレクトリです (UNIX ライクのシステムでは /tmp です)。
アップロードされたファイルに設定するファイルモードで、数字で表現 (例: 0644) します。ファイルモードの意味は os.chmod のドキュメント を参照してください。
この値を設定しないか、 None にすると、アップロードされたファイ ルのモードはオペレーティングシステムに依存します。ほとんどのプラッ トフォームでは、一時ファイルのファイルモードは 0600 で、メモリ からファイルにデータを書き出すときにはシステム標準の umask を使いま す。
Warning
ファイルモードにあまりくわしくないのなら、先頭の 0 がとても 重要だということに注意してください。先頭の 0 は、値が 8 進数で あることを示しています。 644 のように指定すると、全くおかし な挙動になってしまうでしょう。
ファイルモードの先頭には常に ``0`` をつけてください.
アップロードファイルを処理する実際のハンドラです。 この設定を変更すると、ファイルアップロードの処理を完全にカスタマイ ズでき、 Django のアップロード処理全体の置き換えすらできます。 詳しくは後述の アップロードハンドラ を参照してください。
デフォルト値は以下の通りです:
("django.core.files.uploadhandler.MemoryFileUploadHandler",
"django.core.files.uploadhandler.TemporaryFileUploadHandler",)
このデフォルトの設定では、アップロードされたファイルをまずメモリに 保存しようと試み、その後ファイルへの保存を試みます。
File から継承したものの他に、 UploadedFile オブジェクトは、以 下のメソッドや属性を定義しています。
Note
通常の Python のファイルオブジェクトと同じく、アップロードファイルをイ テレータとして扱うと、ファイルの内容を一行づつ読めます:
for line in uploadedfile:
do_something_with(line)
しかし、標準の Python ファイルとは 違って 、 UploadedFile は 改行文字として \n (いわゆる Unix形式) しか認識しません。 \n 以 外の改行で終端されたアップロードファイルを処理する必要があるのなら、ビュー 内で適切に処理する必要があります。
ユーザがファイルをアップロードすると、 Django はファイルデータを アップロードハンドラ (upload handler) と呼ばれるクラスに渡します。このク ラスはアップロードされたファイルデータを処理するためのクラスです。 デフォルトのアップロードハンドラは FILE_UPLOAD_HANDLERS で以下のように 定義されています:
("django.core.files.uploadhandler.MemoryFileUploadHandler",
"django.core.files.uploadhandler.TemporaryFileUploadHandler",)
MemoryFileUploadHandler と TemporaryFileUploadHandler は、Django の デフォルトのファイルアップロード処理、すなわち、小さいファイルはメモリに読 み込み、大きいファイルはディスクに読み込むという動作を実現します。
カスタムのハンドラを書けば、ファイルの処理方法をカスタマイズできます。例え ば、カスタムハンドラによってユーザレベルで容量制限を課したり、データをオン ザフライで圧縮したり、プログレスバーを描画したり、受け取ったデータをローカ ルに保存せず、直接別のストレージに送ったりできます。
ビューごとに別々のアップロード処理を行いたい場合があります。その場合、 リクエストごとに request.upload_handlers を変更してアップロードハンドラ のチェインをオーバライドできます。デフォルトでは、 request.upload_handlers には 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 はエラーを送 出します。
従って、アップロードハンドラチェインはビュー処理の出来るだけ早い段階で 変更しておくことになるでしょう。
ファイルアップロードハンドラは、全て django.core.files.uploadhandler.FileUploadHandler のサブクラスでなけれ ばなりません。アップロードハンドラはどこに記述してもかまいません。
カスタムのファイルアップロードハンドラは以下のメソッドを 必ず 定義せねばなりません:
ファイルアップロード時に、データの「チャンク」を受け取ったときに呼 び出されます。
raw_data はアップロードされたデータの入ったバイト文字列です。
start は raw_data チャンクのファイルデータ中の開始位置です。
このメソッドが返すデータは、次のアップロードハンドラの receive_data_chunk メソッドにフィードされます。この仕組みによっ て、あるハンドラにデータを「フィルタ」させ、他のハンドラに入力でき ます。
receive_data_chunk で受け取ったデータを後続のハンドラに処理させ たくない場合には None を返してください。このメソッドは、アップ ロードされたデータを自分で処理して、他のハンドラにデータを保存させ たくない場合に便利です。
StopUpload や SkipFile といった例外を送出すると、アップロー ド処理は中断し、アップロードファイルの処理を行いません。
ファイルのアップロードが終了したときに呼び出されます。
このハンドラは request.FILES に入れるための UploadedFile オ ブジェクトを返さねばなりません。後続のアップロードハンドラに UploadedFile を返させたい場合は None を返してください。
カスタムのアップロードハンドラでは、以下のオプションのメソッドや属性を定義 できます:
Django がアップロードファイルをメモリ上に読み込み、ハンドラに渡すと きに使うチャンクのサイズです。バイト単位です。この属性は、 FileUploadHandler.receive_data_chunk で読み込まれるチャンクのサ イズ指定でもあります。
パフォーマンスを最大化するには、チャンクのサイズを 4 の倍数とし、 2GB (231 バイト) 以下とすべきです。複数のハンドラがそれぞ れ別々のチャンクサイズを提供していた場合、 Django は最小のチャンク サイズを使います。
デフォルト値は 64*210 バイトまたは 64 KB です。
新たなファイルアップロードが開始されるときに呼び出されるコールバッ クです。このメソッドは、アップロードハンドラがまだデータを受け取っ ていない段階で呼び出されます。
field_name はファイルの <input> フィールドの名前です。
file_name は unicode のファイル名としてブラウザから提供された値 です。
content_type は 'image/jpeg' のような MIME タイプで、ブラウ ザから提供された値です。
content_length はファイルの長さで、ブラウザから提供された値です。 ブラウザがファイルサイズを提供しないこともあり、その場合は None です。
charset は (utf8 のような) 文字セットで、ブラウザから提供さ れた値です。 content_length と同様、指定されないこともあります。
他のハンドラにファイルを処理させたくない場合、このメソッドから StopFutureHandlers 例外を送出してもかまいません。
このメソッドを使うと、生の HTTP 入力の解析を完全にオーバライドでき ます。
input_data は read() をサポートするファイルライクオブジェク トです。
META は request.META と同じです。
content_length は input_data に入っているデータの長さです。 input_data から content_length バイト以上を読み出そうとして はなりません。
boundary はリクエストの MIME バウンダリです。
encoding はリクエストのエンコーディングです。
アップロードの処理を他のハンドラに継続させたい場合には None を、 リクエストの直接処理に適したデータ構造を新たに生成して返したければ (POST, FILES) のタプルを返してください。
Aug 31, 2012