XMLHttpRequest でファイルのアップロードができないか?
ブックマーク経由でみかけたCodeWeb: 画像をアップロードする前にサムネイルを表示させる。 では、JavaScript を使ってファイルをアップロードする前にプレビューする方法が紹介されてました。input type="file" な input 要素の onchange ハンドラでごにょるというもの。
一方で、プレビューではなく、ファイルを追加した直後に特にボタンなどを押さず且つ画面遷移なしでファイルをアップロードできないものかと、ちょっと試行錯誤してみています。例によって XMLHttpRequest で Ajax るわけですが、残念ながらいまのところうまくいってません。
これまでに書いたコードは以下です。prototype.js を使ってます。
<input type="file" name="image" onchange=" if (this.value) { new Ajax.Request( '/upload', { parameters : Form.serialize(this.form), requestHeaders : ['Content-Type', 'multipart/form-data' ], onLoading : function(request) { Element.toggle($('indicator')); }, onComplete : function(request) { var result = eval(request.responseText); if (!result.error) { $('message').innerHTML = '成功したよ'; Element.toggle($('indicator')); } else { $('message').innerHTML = '失敗してるし'; Element.toggle($('indicator')); } } }); } ">
まず普段と異なるのは Content-Type で、ファイルをアップロードするので "multipart/form-data" を指定します。これは Ajax.Request の requestHeaders で指定すれば ok。
ところで RFC などによるとmultipart/form-data な場合、実際の HTTP POST リクエストの内容は
Content-type: multipart/form-data; boundary=BOUNDARY --BOUNDARY Content-Disposition: form-data; name="image"; filename="C:\foobar\barbaz.jpg" Content-Type: application/octet-stream (ファイルの中身) --BOUNDARY
といった感じで送る必要があるようです。つまり、デフォルトの application/x-www-form-urlencoded の場合とはちょっと異なると。
もしかしたら XMLHttpRequest に multipart/form-data を指定してやればこの辺をよしなに扱ってくれるかなと期待してみましたがやっぱりだめ。そもそも、Form.serialize のように application/x-www-form-urlencoded を前提にしたロジック使ってるわけで、このコードじゃ動かないのは当然ということになってくる。
そこでパラメータを組み立てるものは使わずに、自力で multipart な HTTP リクエストを作って XMLHttpRequest に乗せてみよう、と思ったのもつかの間、ファイルの中身を JavaScript で取得するにはローカルのファイルにアクセスする必要があって、これは普通の方法じゃできない(はず)ので、困って途方にくれた...というところです。この辺 で同じようなことをやってるっぽいのですが、これは XUL のサンプルで、XUL ならローカルファイルにアクセスする手段がある模様。
なんとかしてブラウザに先にローカルファイルを取得させつつそれを JavaScript で横取りする、あるいは XMLHttpRequest で multipart なリクエストを処理させる方法はないものでしょうか?