TL; DR;
画像(主にjpeg、png、gif)をアップロードする前に、ブラウザ側で直接圧縮する方法はありますか? JavaScriptはこれを実行できると確信していますが、それを達成する方法を見つけることができません。
これは、実装したい完全なシナリオです。
input type="file"
要素を介して画像を選択します。最後のステップまでの完全なプロセスはクライアント側で実行する必要があり、最新のChromeとFirefox、Safari 5+およびIE 8 +。可能であれば、JavaScriptのみを使用する必要があります(ただし、これは不可能だと確信しています)。
私は今何もコーディングしていませんが、すでに考えました。ローカルでのファイル読み取りは File API を介して可能であり、画像のプレビューと編集は Canvas 要素を使用して行うことができますが、画像圧縮部分を行う方法が見つかりません。
html5please.com および caniuse.com によると、これらのブラウザのサポートは非常に難しい(IEのおかげ)が、できた FlashCanvas や FileReader などのポリフィルを使用します。
実際には、目標はファイルサイズを小さくすることなので、画像圧縮を解決策と考えています。しかし、アップロードされた画像は毎回同じ場所で私のウェブサイトに表示されることを知っており、この表示領域の大きさ(例えば200x400)を知っています。そのため、これらのサイズに合わせて画像のサイズを変更し、ファイルサイズを小さくすることができました。この手法の圧縮率がどうなるかはわかりません。
どう思いますか ?何かアドバイスはありますか? JavaScriptでブラウザ側の画像を圧縮する方法を知っていますか?返信いただきありがとうございます。
要するに:
ソース: code 。
@PsychoWoodsの答えは良いです。独自のソリューションを提供したいと思います。このJavaScript関数は、画像データのURLと幅を受け取り、新しい幅に合わせてスケーリングし、新しいデータURLを返します。
// Take an image URL, downscale it to the given width, and return a new image URL.
function downscaleImage(dataUrl, newWidth, imageType, imageArguments) {
"use strict";
var image, oldWidth, oldHeight, newHeight, canvas, ctx, newDataUrl;
// Provide default values
imageType = imageType || "image/jpeg";
imageArguments = imageArguments || 0.7;
// Create a temporary image so that we can compute the height of the downscaled image.
image = new Image();
image.src = dataUrl;
oldWidth = image.width;
oldHeight = image.height;
newHeight = Math.floor(oldHeight / oldWidth * newWidth)
// Create a temporary canvas to draw the downscaled image on.
canvas = document.createElement("canvas");
canvas.width = newWidth;
canvas.height = newHeight;
// Draw the downscaled image on the canvas and return the new data URL.
ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0, newWidth, newHeight);
newDataUrl = canvas.toDataURL(imageType, imageArguments);
return newDataUrl;
}
このコードは、データURLがあり、縮小画像のデータURLが必要な場所であればどこでも使用できます。
私は他の答えから2つのものが欠けているのを見ます:
canvas.toBlob
(使用可能な場合)は、canvas.toDataURL
よりもパフォーマンスが高く、非同期です。次のスクリプトは両方の点を扱います。
// From https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob, needed for Safari:
if (!HTMLCanvasElement.prototype.toBlob) {
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
value: function(callback, type, quality) {
var binStr = atob(this.toDataURL(type, quality).split(',')[1]),
len = binStr.length,
arr = new Uint8Array(len);
for (var i = 0; i < len; i++) {
arr[i] = binStr.charCodeAt(i);
}
callback(new Blob([arr], {type: type || 'image/png'}));
}
});
}
window.URL = window.URL || window.webkitURL;
// Modified from https://stackoverflow.com/a/32490603, cc by-sa 3.0
// -2 = not jpeg, -1 = no data, 1..8 = orientations
function getExifOrientation(file, callback) {
// Suggestion from http://code.flickr.net/2012/06/01/parsing-exif-client-side-using-javascript-2/:
if (file.slice) {
file = file.slice(0, 131072);
} else if (file.webkitSlice) {
file = file.webkitSlice(0, 131072);
}
var reader = new FileReader();
reader.onload = function(e) {
var view = new DataView(e.target.result);
if (view.getUint16(0, false) != 0xFFD8) {
callback(-2);
return;
}
var length = view.byteLength, offset = 2;
while (offset < length) {
var marker = view.getUint16(offset, false);
offset += 2;
if (marker == 0xFFE1) {
if (view.getUint32(offset += 2, false) != 0x45786966) {
callback(-1);
return;
}
var little = view.getUint16(offset += 6, false) == 0x4949;
offset += view.getUint32(offset + 4, little);
var tags = view.getUint16(offset, little);
offset += 2;
for (var i = 0; i < tags; i++)
if (view.getUint16(offset + (i * 12), little) == 0x0112) {
callback(view.getUint16(offset + (i * 12) + 8, little));
return;
}
}
else if ((marker & 0xFF00) != 0xFF00) break;
else offset += view.getUint16(offset, false);
}
callback(-1);
};
reader.readAsArrayBuffer(file);
}
// Derived from https://stackoverflow.com/a/40867559, cc by-sa
function imgToCanvasWithOrientation(img, rawWidth, rawHeight, orientation) {
var canvas = document.createElement('canvas');
if (orientation > 4) {
canvas.width = rawHeight;
canvas.height = rawWidth;
} else {
canvas.width = rawWidth;
canvas.height = rawHeight;
}
if (orientation > 1) {
console.log("EXIF orientation = " + orientation + ", rotating picture");
}
var ctx = canvas.getContext('2d');
switch (orientation) {
case 2: ctx.transform(-1, 0, 0, 1, rawWidth, 0); break;
case 3: ctx.transform(-1, 0, 0, -1, rawWidth, rawHeight); break;
case 4: ctx.transform(1, 0, 0, -1, 0, rawHeight); break;
case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
case 6: ctx.transform(0, 1, -1, 0, rawHeight, 0); break;
case 7: ctx.transform(0, -1, -1, 0, rawHeight, rawWidth); break;
case 8: ctx.transform(0, -1, 1, 0, 0, rawWidth); break;
}
ctx.drawImage(img, 0, 0, rawWidth, rawHeight);
return canvas;
}
function reduceFileSize(file, acceptFileSize, maxWidth, maxHeight, quality, callback) {
if (file.size <= acceptFileSize) {
callback(file);
return;
}
var img = new Image();
img.onerror = function() {
URL.revokeObjectURL(this.src);
callback(file);
};
img.onload = function() {
URL.revokeObjectURL(this.src);
getExifOrientation(file, function(orientation) {
var w = img.width, h = img.height;
var scale = (orientation > 4 ?
Math.min(maxHeight / w, maxWidth / h, 1) :
Math.min(maxWidth / w, maxHeight / h, 1));
h = Math.round(h * scale);
w = Math.round(w * scale);
var canvas = imgToCanvasWithOrientation(img, w, h, orientation);
canvas.toBlob(function(blob) {
console.log("Resized image to " + w + "x" + h + ", " + (blob.size >> 10) + "kB");
callback(blob);
}, 'image/jpeg', quality);
});
};
img.src = URL.createObjectURL(file);
}
使用例:
inputfile.onchange = function() {
// If file size > 500kB, resize such that width <= 1000, quality = 0.9
reduceFileSize(this.files[0], 500*1024, 1000, Infinity, 0.9, blob => {
let body = new FormData();
body.set('file', blob, blob.name || "file.jpg");
fetch('/upload-image', {method: 'POST', body}).then(...);
});
};
編集:この回答に関するMr Meのコメントによれば、JPG/WebP形式で圧縮が利用可能になったようです( https://developer.mozilla.org/en-US/docs/Web/API/ HTMLCanvasElement/toDataURL )。
私の知る限り、キャンバスを使用して画像を圧縮することはできません。代わりに、サイズを変更できます。 canvas.toDataURLを使用しても、使用する圧縮率を選択できません。あなたが望むものを正確に行うcanimageを見ることができます: https://github.com/nfroidure/CanImage/blob/master/chrome/canimage/content/canimage.js
実際、画像のサイズを小さくするために画像のサイズを変更するだけで十分な場合がよくありますが、さらに先に進みたい場合は、新しく導入されたメソッドfile.readAsArrayBufferを使用して画像データを含むバッファを取得する必要があります。
次に、DataViewを使用して、画像形式の仕様( http://en.wikipedia.org/wiki/JPEG または http://en.wikipedia。 org/wiki/Portable_Network_Graphics )。
画像データの圧縮に対処するのは難しいでしょうが、もっと悪いことです。一方、PNGヘッダーまたはJPEG exifデータを削除して、画像を小さくすることができます。削除する方が簡単です。
別のバッファーに別のDataWiewを作成し、フィルター処理された画像コンテンツでそれを埋める必要があります。次に、window.btoaを使用して、イメージコンテンツをDataURIにエンコードする必要があります。
あなたが同様のものを実装する場合、私に知らせてください、コードを通過するのは面白いでしょう。
image-conversion 、ここで試してみてください-> デモページ
上記の@ daniel-allen-langdonによって投稿されたdownscaleImage()
関数に問題がありました。画像の読み込みが非同期であるため、image.width
およびimage.height
プロパティがすぐに利用できません。。
これを考慮し、async
関数を使用し、幅だけでなく最長寸法に基づいて画像のサイズを変更する、更新されたTypeScriptの例を参照してください。
function getImage(dataUrl: string): Promise<HTMLImageElement>
{
return new Promise((resolve, reject) => {
const image = new Image();
image.src = dataUrl;
image.onload = () => {
resolve(image);
};
image.onerror = (el: any, err: ErrorEvent) => {
reject(err.error);
};
});
}
export async function downscaleImage(
dataUrl: string,
imageType: string, // e.g. 'image/jpeg'
resolution: number, // max width/height in pixels
quality: number // e.g. 0.9 = 90% quality
): Promise<string> {
// Create a temporary image so that we can compute the height of the image.
const image = await getImage(dataUrl);
const oldWidth = image.naturalWidth;
const oldHeight = image.naturalHeight;
console.log('dims', oldWidth, oldHeight);
const longestDimension = oldWidth > oldHeight ? 'width' : 'height';
const currentRes = longestDimension == 'width' ? oldWidth : oldHeight;
console.log('longest dim', longestDimension, currentRes);
if (currentRes > resolution) {
console.log('need to resize...');
// Calculate new dimensions
const newSize = longestDimension == 'width'
? Math.floor(oldHeight / oldWidth * resolution)
: Math.floor(oldWidth / oldHeight * resolution);
const newWidth = longestDimension == 'width' ? resolution : newSize;
const newHeight = longestDimension == 'height' ? resolution : newSize;
console.log('new width / height', newWidth, newHeight);
// Create a temporary canvas to draw the downscaled image on.
const canvas = document.createElement('canvas');
canvas.width = newWidth;
canvas.height = newHeight;
// Draw the downscaled image on the canvas and return the new data URL.
const ctx = canvas.getContext('2d')!;
ctx.drawImage(image, 0, 0, newWidth, newHeight);
const newDataUrl = canvas.toDataURL(imageType, quality);
return newDataUrl;
}
else {
return dataUrl;
}
}
JPG画像圧縮の場合、JIC(Javascript Image Compression)と呼ばれる最高の圧縮技術を使用できます。これは間違いなく役立ちます-> https://github.com/brunobar79/J-I-C