§ファイルアップロードの処理
§multipart/form-data
を使用したフォームでのファイルアップロード
ウェブアプリケーションでファイルをアップロードする標準的な方法は、ファイル添付データと標準的なフォームデータを混在させることができる特別なmultipart/form-data
エンコーディングを使用したフォームを使用することです。
注記: フォームを送信するために使用するHTTPメソッドは
POST
(GET
ではありません)でなければなりません。
HTMLフォームの作成から始めます
@helper.form(action = routes.HomeController.upload(), Symbol("enctype") -> "multipart/form-data") {
<input type="file" name="picture">
<p>
<input type="submit">
</p>
}
CSRFフィルターを無効にしていない限り、フォームにCSRFトークンを追加します。CSRFフィルターは、フィールドがリストされている順序でマルチパートフォームをチェックするため、ファイル入力フィールドの前にCSRFトークンを配置します。これにより、効率が向上し、ファイルサイズがplay.filters.csrf.body.bufferSize
を超えた場合のトークンが見つからないエラーが回避されます。
次に、multipartFormData
ボディパーサーを使用してupload
アクションを定義します
def upload: Action[MultipartFormData[Files.TemporaryFile]] = Action(parse.multipartFormData) { request =>
request.body
.file("picture")
.map { picture =>
// only get the last part of the filename
// otherwise someone can send a path like ../../home/foo/bar.txt to write to other files on the system
val filename = Paths.get(picture.filename).getFileName
val fileSize = picture.fileSize
val contentType = picture.contentType
picture.ref.copyTo(Paths.get(s"/tmp/picture/$filename"), replace = true)
Ok("File uploaded")
}
.getOrElse {
Redirect(routes.HomeController.index()).flashing("error" -> "Missing file")
}
}
ref
属性は、TemporaryFile
への参照を提供します。これは、multipartFormData
パーサーがファイルアップロードを処理するデフォルトの方法です。
注記: 常に、
anyContent
ボディパーサーを使用し、request.body.asMultipartFormData
として取得することもできます。
最後に、POST
ルーターを追加します
POST / democontrollers.HomeController.upload()
注記: 空のファイルは、ファイルがまったくアップロードされなかった場合と同じように扱われます。これは、
multipart/form-data
ファイルアップロードパートのfilename
ヘッダーが空の場合にも当てはまります(ファイル自体が空でない場合でも)。
§直接ファイルアップロード
サーバーにファイルを送信する別の方法は、Ajaxを使用してフォームから非同期的にファイルをアップロードすることです。この場合、リクエストボディはmultipart/form-data
としてエンコードされず、プレーンなファイルの内容のみが含まれます。
この場合、リクエストボディの内容をファイルに保存するためにボディパーサーを使用するだけで済みます。この例では、temporaryFile
ボディパーサーを使用しましょう。
def upload: Action[Files.TemporaryFile] = Action(parse.temporaryFile) { request =>
request.body.moveTo(Paths.get("/tmp/picture/uploaded"), replace = true)
Ok("File uploaded")
}
§独自のボディパーサーの作成
一時ファイルにバッファリングせずにファイルアップロードを直接処理したい場合は、独自のBodyParser
を作成するだけです。この場合、好きな場所にプッシュできるデータのチャンクを受け取ります。
multipart/form-data
エンコーディングを使用する場合は、FilePartHandler[A]
を提供して異なるSinkを使用してデータを蓄積することで、デフォルトのmultipartFormData
パーサーを引き続き使用できます。たとえば、Accumulator(fileSink)
を指定して、TemporaryFile
ではなくFilePartHandler[File]
を使用できます。
type FilePartHandler[A] = FileInfo => Accumulator[ByteString, FilePart[A]]
def handleFilePartAsFile: FilePartHandler[File] = {
case FileInfo(partName, filename, contentType, dispositionType) =>
val perms = java.util.EnumSet.of(OWNER_READ, OWNER_WRITE)
val attr = PosixFilePermissions.asFileAttribute(perms)
val path = JFiles.createTempFile("multipartBody", "tempFile", attr)
val file = path.toFile
val fileSink = FileIO.toPath(path)
val accumulator = Accumulator(fileSink)
accumulator.map {
case IOResult(count, status) =>
FilePart(partName, filename, contentType, file, count, dispositionType)
}(ec)
}
def uploadCustom: Action[MultipartFormData[File]] = Action(parse.multipartFormData(handleFilePartAsFile)) {
request =>
val fileOption = request.body.file("name").map {
case FilePart(key, filename, contentType, file, fileSize, dispositionType, _) =>
file.toPath
}
Ok(s"File uploaded: $fileOption")
}
§一時ファイルのクリーンアップ
ファイルのアップロードでは、TemporaryFile
APIを使用します。これは、ref
属性を通じてアクセスできる一時ファイルシステムにファイルを格納することに依存しています。すべてのTemporaryFile
参照はTemporaryFileCreator
トレイトから取得され、実装は必要に応じて交換でき、利用可能な場合はStandardCopyOption.ATOMIC_MOVE
を使用するatomicMoveWithFallback
メソッドがあります。
ファイルのアップロードは、無制限のファイルアップロードによりファイルシステムがいっぱいになる可能性があるため、本質的に危険な操作です。そのため、TemporaryFile
の背後にある考え方は、それが完了時にのみスコープ内にあり、できるだけ早く一時ファイルシステムから移動する必要があるということです。移動されない一時ファイルはすべて削除されます。
ただし、特定の条件下では、ガベージコレクションがタイムリーに発生しません。そのため、Pekkoスケジューラを使用してスケジュールされたベースで一時ファイルを削除できるplay.api.libs.Files.TemporaryFileReaper
もあります。これはガベージコレクションの方法とは異なります。
リーパーはデフォルトで無効になっており、application.conf
の設定を介して有効になります。
play.temporaryFile {
reaper {
enabled = true
initialDelay = "5 minutes"
interval = "30 seconds"
olderThan = "30 minutes"
}
}
上記の構成では、「olderThan」プロパティを使用して、30分以上経過したファイルを削除します。アプリケーションの起動後5分後にリーパーが開始され、その後30秒ごとにファイルシステムをチェックします。リーパーは既存のファイルアップロードを認識しないため、システムが慎重に構成されていない場合、長期的なファイルアップロードはリーパーに遭遇する可能性があります。
次へ: SQLデータベースへのアクセス
このドキュメントにエラーを発見しましたか?このページのソースコードはこちらにあります。ドキュメントガイドラインを読んだ後、プルリクエストを自由に送ってください。質問やアドバイスを共有したいですか?コミュニティフォーラムにアクセスして、コミュニティとの会話を始めましょう。