ダウンロードして復号までの流れ
ファイルのダウンロード時にはlocation.
location.
ファイルをダウンロードする
XMLHttpRequestを使って暗号化済みのファイルをダウンロードします。responseTypeを指定することで、
XMLHttpRequestで従来よりよく使われるresponseTextは、
- あいうえお
とUTF-8で書かれたファイルを受信した場合、
req = new XMLHttpRequest;
req.open("GET", "test.txt", false);
req.send();
req.responseText.length; // 5 文字
req = new XMLHttpRequest;
req.responseType = "arraybuffer";
req.open("GET", "test.txt", false);
req.send();
req.response.byteLength; // 15bytes
それに対して、req.
はリスト4のように5文字を示します。文字数とバイト数が一致しないと、
今までXMLHttpRequestを使ってバイナリファイルを扱うには、charset = x-user-defined
という文字コードを指定する方法がよく知られていました
特殊な文字コード指定を使うことで、
ファイルのダウンロードと復号
単純にXMLHttpRequestで巨大なファイルをダウンロードする場合、
この問題はサーバ側でファイルを分割してダウンロードすることで解決できます。問題は、
crypto-jsでは受信したデータを順次復号していくことが可能ですが、
ほかのブラウザでは動かないことになってしまうので、
function download(path, password, filename){
req = new XMLHttpRequest;
req.open("GET", "./files/" + path, true);
req.responseType = "arraybuffer";
req.onload = function(){
var buffer = req.response;
decode_arraybuffer(buffer, password, filename);
};
req.send("");
}
function objecturlready(url, filename){
var el;
if (/(gif|jpg|png)$/i.test(filename)) {
el = TB("img", {title: filename, alt: filename, src: url});
} else {
el = TB("a", {href: url, download: filename}, "Click here to save file");
}
document.getElementById("result").appendChild(el);
}
function decode_arraybuffer(ab, password, filename) {
var dec = new StreamDecryptor(password);
dec.decorder = null;
var wordarray = CryptoJS.lib.WordArray.create( ab );
dec.process(wordarray);
dec.process();
var isImage = /(gif|jpg|png)$/i.test(filename);
var suffix = filename.match(/([^.]*?)$/);
suffix = suffix ? suffix[1].toLowerCase() : "";
var mimetype = isImage ? { type: "image/"+suffix}
: { type: "application/octet-stream"};
if (window.requestFileSystem) {
var file = new TempFile();
file.onready = function(){
file.write( new Blob(dec.result) );
setTimeout(function(){
objecturlready(file.toURL(), filename);
}, 100);
};
} else {
// Safari does not support create blob from typed array
var isBuggyBrowser = (new Blob([new Uint8Array()]).size > 0 ) ? true : false;
var blob = isBuggyBrowser
? new Blob(dec.result.map(function(v){ return v.buffer }), mimetype)
: new Blob(dec.result, mimetype);
var objectURL = (
window.URL || window.webkitURL || dataURLsim
).createObjectURL(blob);
setTimeout(function(){
objecturlready(objectURL, filename);
}, 0);
}
}
FileSystem APIを使って一時ファイルを作る
次に、
window.requestFileSystem = window.requestFileSystem ||
window.webkitRequestFileSystem;
// for chrome
if (window.requestFileSystem) {
// var file = new TempFile; file.write(); file.
downloadLink;
var TempFile = function(){
var self = {
ready: false
};
var errorHandler = function(){ console.log(arguments)
};
var onInitFs = function(fs) {
var fileHandler = function(fileEntry) {
self.fileEntry = fileEntry;
// Create a FileWriter object for our FileEntry
fileEntry.createWriter(function(fileWriter) {
fileWriter.onwriteend = function(e) {
console.log('Write completed.') };
fileWriter.onerror = function(e) {
console.log('Write failed: ' + e.toString())
};
// Create a new Blob and write it to log.txt.
self.ready = true;
self.writer = fileWriter;
self.seek = function(pos) { fileWriter.seek(pos) };
self.write = function(blob) {
fileWriter.write(blob) };
self.append = function(blob) {
self.seek(fileWriter.length);
self.write(blob);
};
self.toURL = function(){
return self.fileEntry.toURL() };
if (self.onready) {
self.onready();
}
}, errorHandler);
}
fs.root.getFile(
'data', // ファイル名
{create: true}, fileHandler , errorHandler);
};
window.requestFileSystem(
window.TEMPORARY, // 一時領域
1024*1024, onInitFs, errorHandler);
return self;
}
}
サイズの小さいデータならば、
ファイルをURLに変換する
createObjectURLを使うことで、
名前を付けてファイルを保存
a
タグのdownload attribute
というものがWHATWGで提案されており、download="ファイル名"
とすることで、
Data URIで代用する
比較的小さなファイルや、
var dataURLsim = {
createObjectURL: function(blob){
var fr = new FileReader;
fr.readAsDataURL(blob);
return {
toString: function(){ return fr.result }
};
}
};
var objectURL = (
window.URL || window.webkitURL || dataURLsim
).createObjectURL(blob);
setTimeout(function(){ img.src = objectURL }, 0);
本来使うべきcreateObjectURLと違ってFileReaderを使うため、
ダウンロード復号の際、特に気を付けたいポイント
次に、
Blobの組み立て
Blobを生成するためには、
// 文字列からBlob を作る
// 長さ1 の内部表現文字列がUTF-8 にエンコードされて
// 3bytes のBlob になった
new Blob([" あ"]).size // 3bytes
// 長さ1 の内部表現文字列をUTF-8 のバイト列に変換する
inta = new Uint8Array( encodeURI(" あ").split("%").
slice(1).map(function(v){ return parseInt(v,16)}) )
// 長さ3 のUint8Array がそのままBlob に変換された
new Blob([inta]).size // 3bytes
// " あ" を3 文字に分解する
bytes = encodeURI(" あ").split("%").slice(1).
map(function(v){ return String.fromCharCode(
parseInt(v,16) ) }).join("");
bytes.length // 3 文字
// 長さ3 の内部表現文字列をUTF-8 にエンコードした結果
// 2×3 で6bytes になった
new Blob([bytes]).size // 6bytes
従来の
Typed ArrayからのBlob生成
Blobコンストラクタに直接渡してBlobオブジェクトを生成できます。しかし、
// "\x01\x02\x03\x04\x05" 相当のバイナリデータを作る
var array = new Uint8Array([1,2,3,4,5]);
new Blob([array]) // 現在推奨されている方法 [12]
new Blob([array.buffer]) // 古い方法 [13]
SafariでBlobコンストラクタに空のTyped Arrayを渡した場合、
b = new Blob([new Int8Array()]);
f = new FileReader;f.readAsText(b);
f.result; // "[object Int8Array]"
このように、
Typed ArrayからBlobの生成に対応しているかどうか見極めるためには、
var isOldBlobConstructor = (
new Blob([new Int8Array()]).size > 0 ) ? true : false;