jQueryはAJAX関連のメソッドが充実しているものの、デフォルトではファイルのアップロードをAJAXで行うことはできません。
AJAXでファイルアップロードを行うためのプラグインは数あれど AjaxFileUpload がよさげなので使ってみることにしました。
AjaxFileUpload
http://www.phpletter.com/Our-Projects/AjaxFileUpload
上のリンクのHow to use it? を参考にすれば簡単にAjaxでファイルのアップロードを行うことが可能なのですが、今回はちょっとひと工夫を。
通常は...
1. ファイルを選択
2. 「アップロードボタン(submitボタン)」をクリック
という流れでファイルのアップロードを行います。
ボタンをクリックするの面倒じゃない?という方のために、ファイルを選択した時点でアップロードを実行するようにしてみます。
HTML
<form action="/profile/icon/upload/" enctype="multipart/form-data" id="upload_form" method="post">
<input type="file" name="icon" id="icon_field"/>
</form>
<div id="icon_container">
<img src="default_icon.gif" alt="icon" />
</div>Javascript
<script src="/javascripts/ajaxfileupload.js" type="text/javascript"></script>
<script type="text/javascirpt">
$(document).ready(function() {
$('#upload_form').submit(function(){
var $this = $(this);
$.ajaxFileUpload({
url: $this.attr('action'),
secureuri:false,
fileElementId: 'icon_field',
dataType: 'json',
success: function (obj, status) {
if (obj.success) {
$('#icon_container').find('img').attr('src', '/icon/' + obj.key);
} else if(obj.error && obj.message){
alert(obj.message);
}
$this.find('input:file').bind('change', triggerUpload);
},
error: function (data, s, e) {
alert('Sorry, an error occured.');
}
});
return false;
});
$('#icon_field').change(triggerUpload);
});
function triggerUpload() {
$('#upload_form').trigger('submit');
}
</script>アップロードが成功したら動的に画像を変更します。
ポイントはtriggerUploadという関数を用意しておいて、Ajax成功後にsuccessの中で再バインドしているところでしょうか。
AjaxFileUploadはAjax通信時にファイルフィールド(fileElementIdで指定した要素)のIDを動的に変更するようです。
ですので、$(document).readyで最初にchangeイベントをバインドしていても、一度Ajaxリクエストを行うとイベントが効かなくなりますのでご注意を。
Serverサイド (ここではPython(Django0.96)を使用)
from app import models
from django.http import HttpResponse, Http404
from django.utils import simplejson
from google.appengine.api import images
def upload_icon(request):
res = {}
if request.method != 'POST' or not request.FILES.has_key('icon') or 'content-type' not in request.FILES['icon']:
raise Http404
main, sub = request.FILES['icon']['content-type'].split('/')
if not (main == 'image' and sub in ['jpeg', 'pjpeg', 'gif', 'png']):
res = {'error': 1, 'message': 'JPEG, PNG, GIF only.'}
else:
try:
prof = request.user.get_profile()
except:
prof = models.Profile()
prof.user = request.user
content = request.FILES['icon']['content']
if sub == 'jpeg' or sub == 'pjpeg':
output_encoding = images.JPEG
content_type = 'jpeg'
else:
output_encoding = images.PNG
content_type = 'png'
prof.icon = images.resize(content, 50, 50, output_encoding)
prof.content_type = content_type
prof.save()
res = {'success': 1, 'key': str(prof.key())}
response = simplejson.dumps(res, ensure_ascii=False)
return HttpResponse(response)ここではアップロードされた画像を50×50にリサイズしてストレージ(Datastore)に保存しています。
(Google App Engineで試したので通常のDjangoにはないモジュールを使用しています)
ポイントは、JSONを返すといってもレスポンスのヘッダーをtext/javascirptなどにしてしまわないこと。
(上の例ではtext/htmlで返しています。)
ヘッダーをtext/javascirptとしてしまうと、JSONが<pre>タグで囲まれてしまいます。
(ファイルをAjaxでアップロードするためにiframeを利用しているため!?)
<pre>{'success': 1, 'key': 'abcdefg'}</pre>専用のサンプルページは用意していませんが、LibroSpotのプロフィール編集ページが上のようになっています。
LibroSpotにログインした後、「My本ログ」の「プロフィールを編集する」リンクに行ってiconのアップロードをしてみてください。

