revision-up-to: | 17812 (1.4) |
---|
多くの 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']
でファイルデータにアクセスできます。
もしリクエストメソッドが POST
であり、尚且つ <form>
において
アトリビュート enctype="multipart/form-data"
を含むリクエストが
ポストされたのであれば、 request.FILES
はデータのみを含んでいる事に注意して下さい。
そうでない場合 request.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
を明示的にフォーム
のコンストラクタに渡さねばならないので注意してください。
これはフォームにファイルデータを結びつけるために必要な手順です。
UploadedFile
¶パズルの最後のピースは request.FILES
からの実際のデータファイルをハンドリングすることです。
この辞書の各エントリーはアップロードされたファイルをシンプルなラッパで
包んだ UploadedFile
オブジェクトです。
通常、アップロードされた内容にアクセスする為にこれらのメソッドのいずれか
を使う事になるでしょう:
read
()¶アップロードされたデータ全体をファイルから読み出します。このメソッ
ドを使うときに十分注意してください。というのも、アップロードされた
ファイルが巨大だと、メモリに読み込む際にシステムの容量を越してしま
うかもしれないからです。そのようなときは、後述の chunks()
を使う
とよいでしょう。
multiple_chunks
()¶ファイルが大きくて、複数のチャンクに分けて読み出すべきである場合に
True
を返します。デフォルトの設定では、 2.5 Mbytes より大きなファ
イルに対して True
を返します。サイズの閾値は設定でき、後で説明
します。
chunks
¶ファイルのチャンクを返すジェネレータです。 multiple_chunks()
が
True
の場合には、 read()
ではなくこのメソッドを使ってくださ
い。
実際には、常に chunks()
を使うのがよいでしょう。後述の例を参照
してください。
name
¶my_file.txt
のような、アップロードされたファイルの名前です。
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 つの設定で制御できます:
FILE_UPLOAD_MAX_MEMORY_SIZE
FILE_UPLOAD_TEMP_DIR
FILE_UPLOAD_MAX_MEMORY_SIZE
より大きなファイルがアップロー
ドされたときにファイルデータを保存するディレクトリです。
デフォルト値はシステム標準の一時ファイルディレクトリです
(UNIX ライクのシステムでは /tmp
です)。
FILE_UPLOAD_PERMISSIONS
アップロードされたファイルに設定するファイルモードで、数字で表現
(例: 0644
) します。ファイルモードの意味は
os.chmod()
のドキュメントを参照してください。
この値を設定しないか、 None
にすると、アップロードされたファイ
ルのモードはオペレーティングシステムに依存します。ほとんどのプラッ
トフォームでは、一時ファイルのファイルモードは 0600
で、メモリ
からファイルにデータを書き出すときにはシステム標準の umask を使います。
Warning
ファイルモードにあまりくわしくないのなら、先頭の 0
がとても
重要だということに注意してください。先頭の 0 は、値が 8 進数で
あることを示しています。 644
のように指定すると、全くおかし
な挙動になってしまうでしょう。
ファイルモードの先頭には常に 0 をつけてください.
FILE_UPLOAD_HANDLERS
アップロードファイルを処理する実際のハンドラです。 この設定を変更すると、ファイルアップロードの処理を完全にカスタマイ ズでき、 Django のアップロード処理全体の置き換えすらできます。 詳しくは後述の アップロードハンドラ を参照してください。
デフォルト値は以下の通りです:
("django.core.files.uploadhandler.MemoryFileUploadHandler",
"django.core.files.uploadhandler.TemporaryFileUploadHandler",)
このデフォルトの設定では、アップロードされたファイルをまずメモリに 保存しようと試み、その後ファイルへの保存を試みます。
UploadedFile
オブジェクト¶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 のファイルオブジェクトと同じく、アップロードファイルをイ テレータとして扱うと、ファイルの内容を一行づつ読めます:
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 はエラーを送
出します。
従って、アップロードハンドラチェインはビュー処理の出来るだけ早い段階で
変更しておくことになるでしょう。
また request.POST
は通常与えられる
CsrfViewMiddleware
によって許可されます。
これはビューの中でアップロードハンドラの変更をする為に
csrf_exempt()
を使う必要がある事を
意味します。
またそのとき csrf_protect()
を
リクエストの実際のプロセスの関数上で使う必要があります。
このときハンドラがCSRFチェックが行われる前にファイルアップロードを受け取
り始める可能性がある事に注意して下さい。
Example code:
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 (231 バイト) 以下とすべきです。複数のハンドラがそれぞれ別々
のチャンクサイズを提供していた場合、 Django は最小のチャンクサイズを使い
ます。
デフォルト値は 64*210 バイトまたは 64 KB です。
FileUploadHandler.new_file(self, field_name, file_name, content_type, content_length, charset)
新たなファイルアップロードが開始されるときに呼び出されるコールバックです。 このメソッドは、アップロードハンドラがまだデータを受け取っていない段階で 呼び出されます。
field_name
はファイルの <input>
フィールドの名前です。
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)
のタプルを返してください。
Oct 26, 2017