キャンバスを使用して画像サイズを圧縮する例
質問元
この問題は、画像ファイルをアップロードする際、バックエンドでは2MBに制限しているのに、カメラを起動して撮影すると、数分で2MBを超えてしまうことに起因しています。
アイデア分析
いろいろ調べてみると、画像の圧縮ができるのはcanvasだけということがわかりました。
原理はおそらく 1、まず画像ファイルをbaseURLに変換する 2、ファイルを受け取るimageタグを作り、画像の幅、高さ、スケールを取得する。3. キャンバスの大きさを設定するために、キャンバスを作成する。4、キャンバスに画像を描きます。5、canvasを圧縮して新しいbaseURLを取得する 6、baseURLをファイルに戻す。
前提条件となる関数
ファイルファイルをbase64に変換する
/**
* @param {binary file stream} file
* @param {callback function that returns base64} fn
*/
function changeFileToBaseURL(file,fn){
// Create a read file object
var fileReader = new FileReader();
// return null if file is not defined
if(file == undefined) return fn(null);
// Read the file, the result is base64 bit
fileReader.readAsDataURL(file);
fileReader.onload = function(){
// read the base64
var imgBase64Data = this.result;
fn(imgBase64Data);
}
}
base64をファイルストリームに変換する
/**
* Convert base64 to file
* @param {baseURL} dataurl
* @param {file name} filename
* @return {file binary stream}
*/
function dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(','), mime = arr[0].match(/:(. *?) ;/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {type:mime});
}
圧縮方式
/**
* canvas compressed image
* @param {parameter obj} param
* @param {file binary stream} param.file must be passed
* @param {target compression size} param.targetSize not passed initial assignment -1
* @param {output image width} param.width don't pass initial assignment -1, isometric scaling don't pass height
* @param {output image name} param.fileName don't pass the initial assignment image
* @param {compressed image degree} param.quality don't pass initial assignment 0.92. value range 0~1
* @param {callback function} param.succ must be passed
*/
function pressImg(param){
// if there is no callback function, do not execute
if(param && param.succ){
//return null if file is not defined
if(param.file == undefined) return param.succ(null);
//append initial values to the parameters
param.targetSize = param.hasOwnProperty("targetSize") ? param.targetSize : -1;
param.width = param.hasOwnProperty("width") ? param.width : -1;
param.fileName = param.hasOwnProperty("fileName") ? param.fileName: "image";
param.quality = param.hasOwnProperty("quality") ? param.quality : 0.92;
var _this = this;
// Get the file type
var fileType = param.file.type;
// console.log(fileType) // image/jpeg
if(fileType.indexOf("image") == -1){
console.log('Please select the image file ^_^');
return param.succ(null);
}
// If the current size is smaller than the target size, output directly
var size = param.file.size;
if(param.targetSize > size){
return param.succ(param.file);
}
// read the file, the result is base64 bit
changeFileToBaseURL(param.file,function(base64){
if(base64){
var image = new Image();
image.src = base64;
image.onload = function(){
// Get the aspect ratio
var scale = this.width / this.height;
// console.log(scale);
// Create a canvas
var canvas = document.createElement('canvas');
// Get the context
var context = canvas.getContext('2d');
//get the width of the compressed image, if width is -1, default original image width
canvas.width = param.width == -1 ? this.width : param.width;
//get the height of the compressed image, if width is -1, the original height will be the default
canvas.height = param.width == -1 ? this.height : parseInt(param.width / scale);
// Draw the image on top of the canvas
context.drawImage(image, 0, 0, canvas.width, canvas.height);
//compress the image and get the new base64Url
var newImageData = canvas.toDataURL(fileType,param.quality);
//convert base64 to a file stream
var resultFile = dataURLtoFile(newImageData,param.fileName);
//judge if targetSize has a limit and the compressed image size is larger than the target size, then pop up an error
if(param.targetSize ! = -1 && param.targetSize < resultFile.size){
console.log("Image upload size is too large, please re-upload ^_^");
param.succ(null);
}else{
//return the file stream
param.succ(resultFile);
}
}
}
});
}
}
メソッド使用
ファイルのサイズはバイト数によって決まるので、要求されたサイズをバイトに変換する必要があります。1バイトは1B、1KB=1024B、1MB=1024 * 1024B
<input type="file" id="fileImg" class="fileImg"/>
// Image file upload get url
$("#fileImg").on('change',function(){
pressImg({
file:this.files[0],
targetSize:2 * 1024 * 1024,
quality:0.5,
width:600,
succ:function(resultFile){
//if not null, compression is successful
if(resultFile){
//TODO
}
}
})
});
課題の概要
画像圧縮レベル
画像の圧縮レベルは簡単に決められるものではないので、需要側の要求に応じて何度か試行錯誤をして調整します。対象となる画像のサイズとシャープネスの両方を変えることで、画像の圧縮度合いを変えることができます。
画像サイズが予想と一致するまで、画像を圧縮する再帰処理をしたかったのですが
すると、以下のことが判明しました。
- ターゲットサイズが小さく、画像を何らかの方法で圧縮して条件を満たすことができない場合、ループの飛び出しに失敗してリソースを浪費することになります。
- 画像を何度も圧縮すると、ファイルサイズはもう変わらないし、逆に増えることもあるので不思議です。だから再帰は断念。
iosのショットの向きの問題
iosのカメラは反時計回りに90度回転させて撮影しています。そして、画像を圧縮してバックエンドに送ったところ、画像のexif情報の撮影方向が抜けていて、iosでアップロードされた画像は全て反時計回りに90度回転していることがわかりました。この問題はAndroidでは見つかりませんでした。
<図
今のところ、base64でファイルに変換する際に紛失した疑いがあります。検証後、ここに追記します。
@version1.0 - 2019-8-2 - "canvasを使った画像サイズの圧縮を作成する。
灼熱の韻律
以上が今回の記事の全内容です。皆様の学習のお役に立てれば幸いです。また、Script Houseをより一層応援していただければと思います。
関連
-
html5でポップアップ画像のクリック機能を実装する
-
モバイルHTML5開発ツール vconsoleの詳細解説
-
amazeuiのツリーノード自動展開パネルの実装と、最初のツリーノードの選択
-
html2canvasのスクリーンショットが空白になる問題の解決法
-
Html5 video タグ 動画のベストプラクティス
-
HTML表示 pdf, word, xls, ppt方式例
-
キャンバス経由でのRGBAフォーマットへの色変換とパフォーマンス問題の解決
-
HTML5でWeb Notificationのデスクトップ通知を実装する方法
-
モバイル版Html5におけるBaidu地図のクリックイベント
-
HTML5動画再生(動画),JavaScript制御動画サンプルコード
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Html5プロジェクト適応システムダークカラーモードプログラム概要の詳細説明
-
HTML5 ドラッグ&ドロップによるファイルアップロードのサンプルコード
-
Canvasでイベントを追加する方法を説明する
-
メソッドステップを構築するフレームワーク「AmazeUI」(グラフィック)
-
キャンバス描画の解像度が拡大され、ぼやけた状態になる
-
Html5は、コンテナは、画面の高さや残りの高さの適応的なレイアウトの実装を埋めることができます
-
キャンバスを使ってWeChatアバターなしの招待状ポスターを生成する
-
IOSキーボードがfocusoutイベントでしまわれたときに元の場所に戻らない問題を解決する
-
HTML5新フォームコントロールとフォームプロパティのサンプルコード詳細
-
html5のfigureとfigcaptionの使い方