この記事では、主にモバイル端末での圧縮画像アップロード機能を実現するための H5 の使用方法を詳しく紹介します。興味のある友人は参考にしてください。
以前、同僚がモバイルでのキャンバス圧縮の使用について話してくれました。最近暇なので写真アップロード機能を実践してみました。デモエフェクトのリンクは記事の下部に掲載されています。
携帯端末に写真をアップロードする場合、ユーザーは携帯電話でローカル写真をアップロードしますが、iPhone6 を例に挙げると、通常、アップロードする場合、多くの写真のサイズは 1 メガバイトまたは 2 メガバイトになります。ユーザーがモバイルデータを使用している場合、画像を完全にアップロードすることは明らかに良い考えではありません。
現在、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に挿入し、formdataを送信しますXmlHttpRequest を通じて。
この3ステップで画像の圧縮とアップロードが完了します。
簡単そうに聞こえますが、実際にはまだ落とし穴がいくつかあります。次に、コードで直接解析してみましょう:
【1】画像データを取得します
まず、画像データを取得します。つまり、入力ファイルの変更イベントをリッスンし、アップロードされたファイルオブジェクトファイルを取得します。 、配列のようなファイルを配列に変換し、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 (!/\/(?:jpegpnggif)/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に変換すると透明な領域が黒くなります。キャンバスのデフォルトは 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; }
[3] 画像のアップロード
画像の圧縮が完了したら、最初にbase64データを文字列に変換し、次にArrayBufferをインスタンス化し、その文字列を変換します。 ArrayBuffer を 8 ビット整数の形式で渡し、BlobBuilder または Blob オブジェクトを通じて 8 ビット整数 ArrayBuffer をバイナリ オブジェクト BLOB に変換し、その BLOB オブジェクトを formdata に追加して、それをバックグラウンドに送信します。アヤックス。
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 + " rel="external nofollow" ">" + 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中文网!
相关推荐:
以上がモバイル端末はH5を使用して画像アップロードの圧縮機能を実装しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。