スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

File APIを利用してファイルアップロード

「5000枚の写真をアップロードできるWebアプリがほしい」と無茶な依頼があったので、HTML5のFile APIを使ってサンプルを作ってみました。

サンプル)
http://5463440d.dotcloud.com/

ソースコード)
https://github.com/jou4/multi_fileupload

※上のサンプルは、サーバーが受け取ったファイルを保存しないのでアップするだけのものです。
※またリクエストのサイズも制限されているので、大きなファイルはアップできません。


ドラッグ&ドロップは、ondragover, ondragend, ondropといったイベントで制御します。
jQueryを使うのであれば、bindメソッドを利用してこんな感じでしょうか。

$("#holder")
.bind("dragover", function(){
this.className = 'hover';
return false;
})
.bind("dragend", function(){
this.className = '';
return false;
})
.bind("drop", function(e){
this.className = '';
handleFiles(e.originalEvent.dataTransfer.files);
e.preventDefault();
return false;
});

ドロップされたら、ファイルオブジェクトのリストをevent.dataTransfer.filesで取得できますが、jQueryの場合だとイベントオブジェクトがラップされているので、originalEventで取り出す必要があります。


ファイル選択ダイアログを利用する場合は、multiple属性を付与することで複数選択が可能になります。

<input id="selector" type="file" name="file[]" multiple />


ダイアログで選択されたらchangeイベントが発生します。要素を指定してfilesでファイルオブジェクトのリストを取り出します。

$("#selector").change(function(){
handleFiles(this.files);
});

ファイルの読み出しにはFileReaderを使用します。画像ファイルの場合だとこんな感じ。

var reader = new FileReader(), img = document.getElementById("image");
reader.onload = function(e){
img.src = reader.result;
};
reader.readAsDataURL(file);

readAsDataURL以外にもいくつかメソッドがありますので状況に応じて使い分けることになります。


今回は大量のファイルをアップロードすることが目的なので、一度に送信せず1ファイルずつ送信するようにしました。FormDataを使ってフォーム送信が可能になります。こんな感じです。

var fd = new FormData();
fd.append("file", file);

$.ajax({
url: "hogehoge",
type: "POST",
data: fd,
processData: false, // dataをそのまま送信させる
contentType: false,
success: function(){}
});


最後に、マルチスレッド風に並列で送信できるようにしてみました。指定されたファイルリストをいくつかのリストへ分割して、それぞれのリストを担当するループを作成する感じです。並列化するとやはり速くなりますね。ざっくりですが並列数=1を基準としたとき、並列数=2で2倍、並列数=3で3倍、並列数=4で3.5倍、並列数=5で4倍になりました。マシンのスペックなどに依存すると思いますので、一概にはいえませんが。

以前Flashを使って、同じようなアップローダーを作成したことがあるのですが、そのときはFileReferenceクラスのbrowseやload、uploadといったメソッドを同時に実行できないといった制限があったので、こういった並列化をしなかった覚えがあります。


まだ対応しているブラウザはChromeとFirefoxといったところだと思いますが、
とりあえず「5000枚の写真をアップロード」という要求には答えられそうです♪
スポンサーサイト

[Javascript+Flash]環境依存メモ

Flash(Actionscript)をJavascriptから操作するというようなことをやっていて
いくつか環境依存な問題が発生したので、メモしておきます。

1)AsからJsに提供する関数名は、Js側で定義済の関数名と重複するとNG(IE)

JavascriptからActionscriptを呼べるように
Actionscript側で以下のような方法で関数を公開することができます。

ExternalInterface.addCallback("hoge", fuga);

上記のhogeと同名の関数がJavascript側に定義されているとIEでエラーが発生しました。



2)Flashを子に持つdiv要素の幅と高さを小さくしたらFlashが消えた(IE6)

Flashをdiv要素の子として配置し、幅や高さを縮めるというようなことをしていたのですが、
ある大きさよりさらに縮めようとした場合、いきなりFlashが消えるという現象が発生しました。

結果的には、div要素のスタイルに、「overflow:hidden;」を設定することで回避できました。

例えば、IEで空のブロック要素を用意した場合、幅や高さを縮めても一定以下には縮まりません。

<div style="width:5px;height:5px"></div>

これは、IEは文字が入っていなくても文字の大きさ分だけサイズを確保しようとするのだとか。
これを狙い通り縮めるには、overflowを設定する必要があります。

空のブロック要素の場合は、単に縮まらないだけなのですが
Flashを子に持つ場合はFlashにまで悪影響を及ぼしてしまうようでした。



3)wmodeを設定する場合は、Flashを表示領域内に配置する(Win + Fx)

透明なFlashを用意して、「top:-100px;left:-100px」として表示領域外に
配置していたのですが、WindowsのFirefoxだけなぜかFlashが動いてくれないという
現象が起きました。

どうやらwmodeを設定する場合には、ブラウザの表示領域にFlashがこないと
レンダリングされないようです。

参考:http://d.hatena.ne.jp/Cherenkov/20090321/p1

透明である必要はなかったので、wmodeの設定をやめたら無事動きました。


[Javascript]jQueryでイベントを処理するときのthis

イベントリスナー中でのthisが指すものが異なるため、どうしたものかと悩んだのでメモしておきます。

prototype.jsならbindAsEventListenerを使用すると解決できるのですが。

div要素内にa要素を追加し、クリックされたらクラス内の変数mynameを表示するサンプルです。

/* sample0 */
$(function(){
  var myobj = new MyClass();
  myobj.create();
});

var MyClass = function(){
  this.myname = 'MyClass';
};
MyClass.prototype = {
  create:function(){
    $('div.target').append(
      $('<a>').text('Click').attr('href', 'javascript:;')
        .click(this.onClick)
    );
  },
  onClick:function(e){
    alert(this.myname);
  }
}


この例では、onClick内でのthisはMyClassインスタンスを指さないため「undefined」と表示されます。

/* sample1 目的のタグに必要な情報は埋めておく */
var MyClass = function(){
  this.myname = 'MyClass';
};
MyClass.prototype = {
  create:function(){
    $('div.target').append(
      $('<a>').text('Click').attr('href', 'javascript:;')
        .attr('myname', this.myname)
        .click(this.onClick)
    );
  },
  onClick:function(e){
    alert($(e.target).attr('myname'));
  }
}


タグに色々情報を持たせるのは少々抵抗があります…

/* sample2 イベント登録時に渡す引数を指定しておく */
var MyClass = function(){
  this.myname = 'MyClass';
};
MyClass.prototype = {
  create:function(){
    $('div.target').append(
      $('<a>').text('Click').attr('href', 'javascript:;')
    );
    $('div.target a').bind('click', {'myname':this.myname}, this.onClick);
  },
  onClick:function(e){
    alert(e.data.myname);
  }
}


$.ajaxを使用する場合は、オプションの中にこっそり潜ませておくような感じでしょうか。

/* sample3 windowオブジェクト内に専用領域を確保する */
var MyClass = function(){
  this.myname = 'MyClass';
  window.myclass = this;
};
MyClass.prototype = {
  create:function(){
    $('div.target').append(
      $('<a>').text('Click').attr('href', 'javascript:;')
        .click(this.onClick)
    );
  },
  onClick:function(e){
    alert(window.myclass.myname);
  }
}


いっそのこと、windowオブジェクト内に持たせてしまう方法です。
複数インスタンスを生成したい場合には困りそうです。

/* sample4 thisを引数として渡す */
var MyClass = function(){
  this.myname = 'MyClass';
};
MyClass.prototype = {
  create:function(){
    var self = this;
    $('div.target').append(
      $('<a>').text('Click').attr('href', 'javascript:;')
        .click(function(e){ return self.onClick(e, self); })
    );
  },
  onClick:function(e, self){
    alert(self.myname);
  }
}


こちらは、イベントリスナーの引数としてthisを渡す方法です。


個人的にはsample4の方法がしっくりきます。

[Javascript]雑多メモ

javascript、jqueryの雑多なメモです。

要素の生成
$('<div id="mypanel" style="background-color:#FFFFDD;">');
//以下のような書き方もできます
$(document.createElement("div"))
.attr("id", "mypanel").css("background-color", "#FFFFDD");

イベントの追加
$(document.createElement("a"))
.attr("id", "mylink")
.attr("href", "javascript:;")
.text("link")
.click(
function(){
//表示
$("#mytextarea").show();
//非表示
$("#mytextarea").hide();
//要素の表示非表示を反転
$("#mytextarea").toggle();
}
);

要素の配置
//mypanelの子要素として末尾へ追加
$("#mypanel").append(mytextarea);
mytextarea.appendTo("#mypanel"); //$("#mypanel")でも動作しました

//mypanelの子要素として先頭へ追加
$("#mypanel").prepend(mytextarea);
mytextarea.prependTo("#mypanel");

//mypanelと並列に後ろへ追加
$("#mypanel").after(mytextarea);
mytextarea.insertAfter("#mypanel");

//mypanelと並列に前へ追加
$("#mypanel").before(mytextarea);
mytextarea.insertBefore("#mypanel");

//テキストエリアの読み書き
var text = $("#mytextarea").val();
$("#mytextarea").val(text);

セレクタとマッチした要素をさらに下位へ走査したい場合
//tr要素を配列として取得
var trList = $("table tr");
//先頭のtr要素の下位のtd要素を配列として取得
var tdList = $("td", trList[0]);

その他
・IE6では、indexOfを配列に対して使用できませんでした。

プロフィール

jou4

Author:jou4
FC2ブログへようこそ!

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。