移動先の画像の圧縮アップロードの例
要旨:前に小さいゲームプラットフォームのプロジェクトをしていましたが、ユーザーセンターのモジュールがあります。モバイル端末の写真をアップロードする時、伝えたのは全部携帯のローカル画像です。ローカルの写真は普通は比較的大きいです。今のスマートフォンにとって、普段たくさんの写真を撮ったのは全部で二、三兆円です。このままアップロードすると、画像が大きすぎます。もしユーザーが使っているのがモバイルの流れなら、完全に画像をアップロードするのは明らかに良い方法ではありません。だからアップロードする前に圧縮処理を行う必要があります。インターネットで多くの資料を探した後、多くの方法を試しました。例えば、Androidがアップロード画像を圧縮できますが、iosでアップロードできません。長い間苦労してやっとiosのピットを発見しました。このようなすでに実践に入ったことがある証明は実行可能で、何兆のピクチャーは最後にすべて私達の後端の要求の200 k以内まで圧縮することができます!このように実行可能な方法はみんなに見てもらわなければなりません。
現在、HTML 5の各種の新しいAPIはモバイル端末のwebkitでより良い実現を得ました。caniuseを見ると、本demoで使用されているFileReader、Blob、Formdataオブジェクトはほとんどのモバイルデバイスのブラウザで実現されています。(safari 6.0+、android 3.0+)ので、直接先端で画像を圧縮して、多くのモバイル端末の画像アップロードの必須機能になりました。
モバイル端末で画像を圧縮してアップロードします。主にfilereader、canvas、formdataの3つのh 5のapiを使います。論理は難しくない。全体の過程は:
(1)ユーザがinput_fileを使って画像をアップロードする場合、ユーザがアップロードした画像データをfilereaderで読み出す(base 64フォーマット)
(2)写真データをカメラに取り込み、canvasに描画し、canvas.toData URLを呼び出して画像を圧縮する。
(3)圧縮されたbase 64フォーマットの画像データを取得し、formdataにバイナリで詰め込み、XmlHttpRequestでformdataを提出する。
この3ステップで、画像の圧縮とアップロードが完了しました。
簡単なようですが、実はちょっと穴があります。次に直接コードで分析します。
【一】画像データの取得
まず画像データを取得し、つまりinput fileのchangeイベントを傍受し、アップロードされたファイルオブジェクトfilesを取得して、クラス群のfilesを配列に変え、forEachを巡回します。
次にファイルタイプを判断し、画像でなければ処理しない。もし写真であれば、filereaderを実装し、アップロードされたファイルデータをbase 64形式で読み込み、データ長を判断し、200 KB以上の画像であれば、comppressメソッドを呼び出して圧縮する。そうでなければ、アップロード方法を呼び出してアップロードする。
上で画像データの取得をしたら、copressで画像を圧縮する方法ができます。圧縮画像も直接絵をcanvasに描いてからtoData URLを呼び出せばいいというものではありません。
IOSにおいて、canvasが絵を描くには二つの制限があります。
まず、画像の大きさは200万画素を超えると、絵もcanvasに描けなくなり、ドラフトImageを呼び出した時には間違いないですが、toData URLで画像データを取得した時には空の画像データが得られます。
なお、canvasの大きさには制限があり、canvasの大きさが大体五百万画素(つまり、幅の高さの積)より大きい場合、絵だけではなく、他のものは全部描けません。
第一の制限に対応して、処理方法は瓦を描いたものです。瓦を描くということは、絵を複数に分割してcanvasに描きます。私のコードでは、絵を100万画素のブロックの大きさに分割して、canvasに描きます。
第二の制限に対応して、私の処理方法は画像の幅の高さを適切に圧縮することです。私のコードの中では保険のために、400万画素を上限に設定しています。400万画素の写真で十分です。幅と高さを合わせて200000ピクセルがあります。
これでIOSにおける二つの制限が解決されました。
上記の制限の他に、もう二つのピットがあります。一つはcanvasのtoData URLはjpgしか圧縮できません。ユーザーがアップロードした画像がpngである場合、jpgに変換しなければなりません。つまり、canvas.toData URL(「image/jpeg」、0.1)を統一してjpegに設定します。
もう一つは、pngからjpgに変換し、canvasに描画すると、canvasが透明領域があると、jpgに変換すると透明領域が黒くなります。canvasの透明画素はデフォルトではrgba(0,0,0,0,0,0,0)となり、jpgに変換するとrgba(0,0,1)になります。つまり、透明背景が黒くなります。解决策は、絵を描く前にキャンバスの上に白い下地を敷いていくことです。
画像の圧縮が完了したら、formdataに入れてアップロードします。まずbase 64のデータを文字列に変換して、ArayBufferを実際に例化して、8ビットの整数の形式でArayBufferに文字列を入れて、BlobbiliderまたはBlobオブジェクトを通して、8ビットの形を整えたArayBufferをバイナリオブジェクトblobに変えて、その後にblob fortaオブジェクトをblobにします。またajaxを通じて楽屋に送ればいいです。
XmlHttpRequest 2では、大きなデータを送信するだけでなく、送信進捗のAPIを取得するなど、コードの中でも簡単に実現しました。
以上のモバイルフロントエンド画像の圧縮アップロードの例は、小編集が皆さんに提供した内容の全てです。参考にしていただければ幸いです。よろしくお願いします。
現在、HTML 5の各種の新しいAPIはモバイル端末のwebkitでより良い実現を得ました。caniuseを見ると、本demoで使用されているFileReader、Blob、Formdataオブジェクトはほとんどのモバイルデバイスのブラウザで実現されています。(safari 6.0+、android 3.0+)ので、直接先端で画像を圧縮して、多くのモバイル端末の画像アップロードの必須機能になりました。
モバイル端末で画像を圧縮してアップロードします。主にfilereader、canvas、formdataの3つのh 5のapiを使います。論理は難しくない。全体の過程は:
(1)ユーザがinput_fileを使って画像をアップロードする場合、ユーザがアップロードした画像データをfilereaderで読み出す(base 64フォーマット)
(2)写真データをカメラに取り込み、canvasに描画し、canvas.toData URLを呼び出して画像を圧縮する。
(3)圧縮されたbase 64フォーマットの画像データを取得し、formdataにバイナリで詰め込み、XmlHttpRequestでformdataを提出する。
この3ステップで、画像の圧縮とアップロードが完了しました。
簡単なようですが、実はちょっと穴があります。次に直接コードで分析します。
【一】画像データの取得
まず画像データを取得し、つまりinput fileのchangeイベントを傍受し、アップロードされたファイルオブジェクトfilesを取得して、クラス群のfilesを配列に変え、forEachを巡回します。
次にファイルタイプを判断し、画像でなければ処理しない。もし写真であれば、filereaderを実装し、アップロードされたファイルデータをbase 64形式で読み込み、データ長を判断し、200 KB以上の画像であれば、comppressメソッドを呼び出して圧縮する。そうでなければ、アップロード方法を呼び出してアップロードする。
filechooser.onchange = function() {
if (!this.files.length) return;
var files = Array.prototype.slice.call(this.files);
if (files.length > 9) {
alert(" 9 ");
return;
}
files.forEach(function(file, i) {
if (!/\/(?:jpeg|png|gif)/i.test(file.type)) return;
var reader = new FileReader();
var li = document.createElement("li");
li.innerHTML = '<div class="progress"><span></span></div>';
$(".img-list").append($(li));
reader.onload = function() {
var result = this.result;
var img = new Image();
img.src = result;
// 200kb,
if (result.length <= maxsize) {
$(li).css("background-image", "url(" + result + ")");
img = null;
upload(result, file.type, $(li));
return;
}
// ,
if (img.complete) {
callback();
} else {
img.onload = callback;
}
function callback() {
var data = compress(img);
$(li).css("background-image", "url(" + data + ")");
upload(data, file.type, $(li));
img = null;
}
};
reader.readAsDataURL(file);
})
};
【2】画像を圧縮する上で画像データの取得をしたら、copressで画像を圧縮する方法ができます。圧縮画像も直接絵をcanvasに描いてからtoData URLを呼び出せばいいというものではありません。
IOSにおいて、canvasが絵を描くには二つの制限があります。
まず、画像の大きさは200万画素を超えると、絵もcanvasに描けなくなり、ドラフトImageを呼び出した時には間違いないですが、toData URLで画像データを取得した時には空の画像データが得られます。
なお、canvasの大きさには制限があり、canvasの大きさが大体五百万画素(つまり、幅の高さの積)より大きい場合、絵だけではなく、他のものは全部描けません。
第一の制限に対応して、処理方法は瓦を描いたものです。瓦を描くということは、絵を複数に分割してcanvasに描きます。私のコードでは、絵を100万画素のブロックの大きさに分割して、canvasに描きます。
第二の制限に対応して、私の処理方法は画像の幅の高さを適切に圧縮することです。私のコードの中では保険のために、400万画素を上限に設定しています。400万画素の写真で十分です。幅と高さを合わせて200000ピクセルがあります。
これでIOSにおける二つの制限が解決されました。
上記の制限の他に、もう二つのピットがあります。一つはcanvasのtoData URLはjpgしか圧縮できません。ユーザーがアップロードした画像がpngである場合、jpgに変換しなければなりません。つまり、canvas.toData URL(「image/jpeg」、0.1)を統一してjpegに設定します。
もう一つは、pngからjpgに変換し、canvasに描画すると、canvasが透明領域があると、jpgに変換すると透明領域が黒くなります。canvasの透明画素はデフォルトではrgba(0,0,0,0,0,0,0)となり、jpgに変換するとrgba(0,0,1)になります。つまり、透明背景が黒くなります。解决策は、絵を描く前にキャンバスの上に白い下地を敷いていくことです。
function compress(img) {
var initSize = img.src.length;
var width = img.width;
var height = img.height;
// , 400
var ratio;
if ((ratio = width * height / 4000000) > 1) {
ratio = Math.sqrt(ratio);
width /= ratio;
height /= ratio;
} else {
ratio = 1;
}
canvas.width = width;
canvas.height = height;
//
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 100
var count;
if ((count = width * height / 1000000) > 1) {
count = ~~(Math.sqrt(count) + 1); //
//
var nw = ~~(width / count);
var nh = ~~(height / count);
tCanvas.width = nw;
tCanvas.height = nh;
for (var i = 0; i < count; i++) {
for (var j = 0; j < count; j++) {
tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);
ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
}
}
} else {
ctx.drawImage(img, 0, 0, width, height);
}
//
var ndata = canvas.toDataURL('image/jpeg', 0.1);
console.log(' :' + initSize);
console.log(' :' + ndata.length);
console.log(' :' + ~~(100 * (initSize - ndata.length) / initSize) + "%");
tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
return ndata;
}
【三】画像のアップロード画像の圧縮が完了したら、formdataに入れてアップロードします。まずbase 64のデータを文字列に変換して、ArayBufferを実際に例化して、8ビットの整数の形式でArayBufferに文字列を入れて、BlobbiliderまたはBlobオブジェクトを通して、8ビットの形を整えたArayBufferをバイナリオブジェクトblobに変えて、その後にblob fortaオブジェクトをblobにします。またajaxを通じて楽屋に送ればいいです。
XmlHttpRequest 2では、大きなデータを送信するだけでなく、送信進捗のAPIを取得するなど、コードの中でも簡単に実現しました。
// , base64 , formdata
function upload(basestr, type, $li) {
var text = window.atob(basestr.split(",")[1]);
var buffer = new ArrayBuffer(text.length);
var ubuffer = new Uint8Array(buffer);
var pecent = 0,
loop = null;
for (var i = 0; i < text.length; i++) {
ubuffer[i] = text.charCodeAt(i);
}
var Builder = window.WebKitBlobBuilder || window.MozBlobBuilder;
var blob;
if (Builder) {
var builder = new Builder();
builder.append(buffer);
blob = builder.getBlob(type);
} else {
blob = new window.Blob([buffer], {
type: type
});
}
var xhr = new XMLHttpRequest();
var formdata = new FormData();
formdata.append('imagefile', blob);
xhr.open('post', '/cupload');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(' :' + xhr.responseText);
clearInterval(loop);
//
$li.find(".progress span").animate({
'width': "100%"
}, pecent < 95 ? 200 : 0, function() {
$(this).html(" ");
});
$(".pic-list").append('<a href="' + xhr.responseText + '">' + xhr.responseText + '<img src="' + xhr.responseText + '" /></a>')
}
};
// , 50%
xhr.upload.addEventListener('progress', function(e) {
if (loop) return;
pecent = ~~(100 * e.loaded / e.total) / 2;
$li.find(".progress span").css('width', pecent + "%");
if (pecent == 50) {
mockProgress();
}
}, false);
// 50%
function mockProgress() {
if (loop) return;
loop = setInterval(function() {
pecent++;
$li.find(".progress span").css('width', pecent + "%");
if (pecent == 99) {
clearInterval(loop);
}
}, 100)
}
xhr.send(formdata);
}
これでアップロードしたフロントエンドの画像を圧縮して完成しました。formdataで提出したので、バックグラウンドでデータを受け取る時は普通のフォームと同じように処理すればいいです。以上のモバイルフロントエンド画像の圧縮アップロードの例は、小編集が皆さんに提供した内容の全てです。参考にしていただければ幸いです。よろしくお願いします。