以下のエディターは、モバイル フロントエンドで画像を圧縮してアップロードする例を共有します。非常に良い参考値となっておりますので、皆様のお役に立てれば幸いです。編集者が来て、一緒に見てみましょう
要約: 私は以前、小さなゲームプラットフォームのプロジェクトに取り組んでいたのですが、アバターをアップロードする機能を含む「ユーザーセンター」モジュールがありました。携帯端末に写真をアップロードする場合、アップロードされる写真はすべて携帯電話のローカル写真であり、現在のスマートフォンを例に取ると、通常、アップロードする写真のサイズは比較的大きいものが多くなります。ユーザーがモバイルデータを使用している場合、画像を完全にアップロードすることは明らかに良い考えではありません。したがって、インターネットで多くの情報を検索した結果、画像を圧縮してアップロードできるのに、iOS ではアップロードできないなど、多くの落とし穴に遭遇しました。 iOS ピットについて知るのに長い時間がかかりました。これは実際に実行可能であることが証明されており、バックエンドで必要な数メガバイトの画像を 200k 以内に圧縮できます。このような実現可能な方法は、皆さんに見せなければなりません(追伸:すべて他人の方法をつなぎ合わせて作られたものです、へへ~)。
現在、HTML5 のさまざまな新しい API がモバイル Webkit に適切に実装されています。 caniuse 氏によると、このデモで使用されている FileReader、Blob、および Formdata オブジェクトは、ほとんどのモバイル デバイスのブラウザー (safari6.0 以降、Android 3.0 以降) に実装されているため、フロントエンドで直接画像を圧縮することが重要になっています。モバイル端末に画像をアップロードする機能。
モバイル端末での画像の圧縮とアップロードには、主にfilereader、canvas、formdataの3つのh5 APIが使用されます。ロジックは難しくありません。全体のプロセスは次のとおりです:
(1) ユーザーが入力ファイルを使用して画像をアップロードする場合、ファイルリーダーを使用してユーザーがアップロードした画像データ (base64 形式) を読み取ります
(2) 画像データを img オブジェクトに渡します。そして、imgをキャンバスに描画し、canvas.toDataURLを呼び出して画像を圧縮します
(3) 圧縮されたbase64形式の画像データを取得し、バイナリに変換してformdataに挿入し、XmlHttpRequestを通じてformdataを送信します。
この3ステップで画像の圧縮とアップロードが完了します。
簡単そうに聞こえますが、実際にはまだ落とし穴がいくつかあります。次に、コードを直接使用して分析します:
[1] 画像データを取得します
まず画像データを取得します。つまり、入力ファイルの変更イベントをリッスンし、アップロードされたファイルを取得します。ファイル オブジェクト ファイルを追加し、配列のような Convert files を配列に追加して、forEach 走査を実行します。
次に、ファイルの種類を決定します。写真でない場合は処理されません。画像の場合は、ファイルリーダーをインスタンス化し、アップロードされたファイル データを Base64 形式で読み取り、データ長を決定します。画像が 200KB より大きい場合は、compress メソッドを呼び出して圧縮し、それ以外の場合は、upload メソッドを呼び出してアップロードします。
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 = '<p class="progress"><span></span></p>'; $(".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】画像を圧縮する
上記の画像データを取得したら、画像を圧縮する方法を使用できます。画像の圧縮には、画像をキャンバスに直接描画してから toDataURL を呼び出す必要はありません。
IOS では、キャンバスに画像を描画する際に 2 つの制限があります:
1 つ目は画像のサイズです。画像のサイズが 200 万ピクセルを超える場合、画像はキャンバスに描画できず、エラーも発生しません。は、drawImage を呼び出すときに報告されますが、toDataURL を使用して画像データを取得すると、空の画像データが取得されます。
さらに、キャンバスのサイズが約500万ピクセル(幅と高さの積)を超えると、絵が描画できないだけでなく、他のものも描画できなくなります。 。
最初の制限に対処するための解決策は、タイルを描画することです。タイル描画とは画像を複数に分割してキャンバス上に描画することですが、私のコードでは画像を100万ピクセルに分割してキャンバス上に描画する方法をとっていました。
2 番目の制限に対処するための解決策は、安全を期すために、画像が 400 万より大きい場合、コードに設定されている上限を 400 万ピクセルに設定することです。ピクセルの場合、4 メガピクセル未満に圧縮されます。 4 メガピクセルの画像で十分なため、幅と高さは 2000X2000 になります。
これにより、IOS の 2 つの制限が解決されます。
上記の制限に加えて、2 つの落とし穴があります。1 つは、canvas の toDataURL が jpg しか圧縮できないことです。ユーザーがアップロードした画像が png である場合、それを jpg に変換する必要があります。つまり、canvas.toDataURL() 'image/jpeg', 0.1)、タイプは一律 jpeg に設定され、圧縮率は独自に制御されます。
もう1つは、pngをjpgに変換する場合、キャンバスに描画するときに、キャンバス内に透明な領域がある場合、jpgに変換すると透明な領域が黒くなります。 Canvas のデフォルトは rgba(0,0, 0,0) なので、jpg に変換すると rgba(0,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里进行上传了,先将base64数据转成字符串,再实例化一个ArrayBuffer,然后将字符串以8位整型的格式传入ArrayBuffer,再通过BlobBuilder或者Blob对象,将8位整型的ArrayBuffer转成二进制对象blob,然后把blob对象append到formdata里,再通过ajax发送给后台即可。
XmlHttpRequest2中不仅可以发送大数据,还多出了比如获取发送进度的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提交,所以后台接数据的时候就跟普通form表单提交数据一样处理即可。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
以上がモバイル フロントエンドの画像圧縮およびアップロード機能を使用するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。