##コンセプトは6つ
・簡単にアップロードできる
・クライアントサイドで画像のサムネイルを作る
・複数のファイルをアップロードできる
・画像をソートしてアップロードできる
・スマホの写真が時々回転してしまうので対策しよう
・そのままスマホの写真を複数アップロードできるようにすると
ファイルサイズがえらいこと(1枚4M!)になったので
クライアント側で画像を縮小
・もちろんAjaxでアップロードだ!
こんなアップローダーを作りたいの。でも、きっと夢ね…。
##偉大なるプログラマーに敬意を!
このアップローダーを作るにあたり以下のプログラムを使わせてもらった。
@stomita
megapix-image.js
https://github.com/stomita/ios-imagefile-megapixel
@jseidelin
exif-js
https://github.com/jseidelin/exif-js
@ibnRubaXa
Sortable.js
http://rubaxa.github.io/Sortable/
使い方については以下の解説が役立った。
@wada811
http://wada811.blogspot.com/2014/11/image-upload--preview.html
もちろんキミへの感謝も忘れないよ!
jQuery
http://jquery.com/
IMAGES = [];
function resizeImage(file, no) {
var d = new $.Deferred();
var mpImg = new MegaPixImage(file);
var src_keeper = document.getElementById('src_keeper');
EXIF.getData(file, function() {
var orientation = file.exifdata.Orientation;
var mpImg = new MegaPixImage(file);
mpImg.render(src_keeper, { maxWidth: 1024, orientation: orientation }, function() {
var resized_img = $(src_keeper).attr('src');
d.resolve(resized_img, no);
});
});
return d.promise();
}
var sortable;
さて、まずはアップローダーの共通コア部分から解説しよう!
・縮小画像のデータを格納するIMAGES
・ソート機能を使うためのsortableをこっそり定義
ここで実現しているのは
・クライアント側での縮小
・EXIF情報を使って画像の回転の修正
の2つ。
画像が完全に読み込まれてから処理をする必要があり、
処理が完全に終わってから次に進むようにする。
$.Deferredの出番だ!
だいたいの部分は上記解説サイトのままだね。
iPhoneは写真の複数選択ができた。
だからHTML部分はこう書いておくよ。
<input id="file" type="file" multiple accept="image/jpeg, image/gif, image/png">
JavaScript部分はこうだ!
$('#file').on('change', function() {
var files_length = this.files.length;
for (var i=0; i<files_length; i++) {
var file = this.files[i];
resizeImage(file, i).then(function(resize_image, no) {
var img = $('<img>');
$(img).css('width', '100px');
img.attr('id', no);
img.attr('src', resize_image);
var canvasData = resize_image.replace(/^data:image\/jpeg;base64,/, '');
IMAGES.push(canvasData);
$('#img').append(img);
// 全ての画像を読み込んだら以下の処理を実行
if ($('#img').find('img').size() == files_length) {
if (files_length > 1) {
sortable = new Sortable(document.getElementById('img'), {
group: 'photo',
animation: 150
});
}
}
});
};
});
縮小が終わったらIMAGESに入れておく。
全ての画像が読み込まれたらソートできるようにする。
どうしてここの処理で$.Deferredを使わないかって?
おっと、次の説明だ!
$('#upload_button').on('click', function() {
var fd = new FormData();
$('#img').find('img').each(function() {
var i = parseInt($(this).attr('id'));
fd.append('base64_encoded_data[]', IMAGES[i]);
});
$.ajax({
url: '/index.php',
type: 'POST',
data: fd,
processData: false,
contentType: false,
dataType: 'json'
})
.done(function(data) {
});
});
FormDataを使ってフォームデータを動的に作成している。
サーバ側にはbase64エンコードされたデータが渡るってことさ。
##さてAndroid版だ!
なんで上でiPhoneだって前置きをしたかというと
Androidでは写真の複数選択ができなかったんだ(ouch!)
なので1枚ずつ選択していくよ。
HTML部分
<input class="submit" id="android_file_select" type="button" value="画像を追加">
JavaScript部分
$('#android_file_select').click(function() {
var hookInput = $('<input type="file" accept="image/jpeg, image/gif, image/png" name="files[]" style="display:none;">');
var id = 'i' + parseInt((new Date)/1000);
hookInput.attr('id', id);
$('#android_file_upload').append(hookInput);
$('#'+id).click();
$(hookInput).on('change', function() {
var i = IMAGES.length;
var file = this.files[0];
resizeImage(file, i).then(function(resize_image, no) {
var img = $('<img>');
$(img).css('width', '100px');
img.attr('id', no);
img.attr('src', resize_image);
var canvasData = resize_image.replace(/^data:image\/jpeg;base64,/, '');
IMAGES.push(canvasData);
$('#android_img').append(img);
if (sortable) {
sortable.destroy();
}
sortable = new Sortable(document.getElementById('android_img'), {
group: 'photo',
animation: 150
});
});
});
});
上に書いた理由からHTMLとJavaScriptの処理自体は
HTTP_USER_AGENTでiPhoneとAndroidに分けざるを得なかった。
画像に対応するinputを動的に作成しているところ以外は
iPhone版と同じかな。
$('#android_upload_button').click(function() {
var fd = new FormData();
$('#android_img').find('img').each(function() {
var i = parseInt($(this).attr('id'));
fd.append('base64_encoded_data[]', IMAGES[i]);
});
$.ajax({
url: '/index.php',
type: 'POST',
data: fd,
processData: false,
contentType: false,
dataType: 'json'
})
.done(function(data) {
});
});
さぁ、これでクールなアップローダーの完成だ!
今回のこの知識がキミの役に立つといいな。