如何在浏览器中通过Javascript压缩图像?
TL;博士强> 在上传图像之前,有没有办法直接在浏览器端压缩图像(主要是jpeg、png和gif)?我很确定JavaScript可以做到这一点,但我找不到实现它的方法如何在浏览器中通过Javascript压缩图像?,javascript,image,cross-browser,compression,Javascript,Image,Cross Browser,Compression,TL;博士 在上传图像之前,有没有办法直接在浏览器端压缩图像(主要是jpeg、png和gif)?我很确定JavaScript可以做到这一点,但我找不到实现它的方法 下面是我想要实现的完整场景: 用户进入我的网站,通过input type=“file”元素选择图像 该图像通过JavaScript检索,我们进行了一些验证,如正确的文件格式、最大文件大小等 如果一切正常,页面上将显示图像预览 用户可以执行一些基本操作,例如将图像旋转90°/-90°,按照预定义的比率进行裁剪等,或者用户可以上载另一
下面是我想要实现的完整场景:
- 用户进入我的网站,通过
元素选择图像input type=“file”
- 该图像通过JavaScript检索,我们进行了一些验证,如正确的文件格式、最大文件大小等
- 如果一切正常,页面上将显示图像预览
- 用户可以执行一些基本操作,例如将图像旋转90°/-90°,按照预定义的比率进行裁剪等,或者用户可以上载另一个图像并返回到步骤1
- 当用户满意时,编辑的图像将被压缩并在本地“保存”(不是保存到文件中,而是保存在浏览器内存/页面中)-
- 用户在表格中填写姓名、年龄等数据
- 用户单击“Finish”按钮,然后将包含数据+压缩图像的表单发送到服务器(不带AJAX)
- 使用HTML5 FileReader API和.readAsArrayBuffer读取文件
- 使用文件数据创建Blob,并使用
- 创建新的Image元素并将其src设置为文件blob url
- 将图像发送到画布。画布大小设置为所需的输出大小
- 通过canvas.toDataURL(“image/jpeg”,0.7)从画布获取缩小的数据(设置自己的输出格式和质量)
- 将新的隐藏输入附加到原始表单,并基本上像普通文本一样传输dataURI图像
- 在后端,读取dataURI,从Base64解码并保存它
来源:。编辑:根据Mr-Me对这个答案的评论,压缩现在似乎可用于JPG/WebP格式(请参阅) 据我所知,您不能使用画布压缩图像,相反,您可以调整其大小。使用canvas.toDataURL将不允许您选择要使用的压缩比。您可以查看canimage,它完全满足您的需求: 事实上,只要调整图像大小以减小其大小通常就足够了,但如果您想更进一步,则必须使用新引入的方法file.readAsArrayBuffer来获取包含图像数据的缓冲区 然后,只需使用DataView根据图像格式规范(or)读取其内容 这将很难处理图像数据压缩,但更糟糕的是一次尝试。另一方面,您可以尝试删除PNG标题或JPEG exif数据以使图像更小,这样做应该更容易 您必须在另一个缓冲区上创建另一个DataWiew,并用过滤后的图像内容填充它。然后,您只需使用window.btoa将图像内容编码为DataURI
如果您实现了类似的功能,请让我知道您是否对代码感兴趣。@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。对于JPG图像压缩,可以使用称为JIC的最佳压缩技术
(Javascript图像压缩)这肯定会帮助您-->我发现其他答案中缺少两件事:
(如果可用)比canvas.toBlob
性能更好,而且也是异步的canvas.toDataURL
- 文件->图像->画布->文件转换丢失EXIF数据;特别是,关于图像旋转的数据通常由现代手机/平板电脑设置
// 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);
}
//来自https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob,用于狩猎:
if(!htmlcanvaseElement.prototype.toBlob){
Object.defineProperty(htmlcanvaseElement.prototype,“toBlob”{
值:函数(回调、类型、质量){
var binStr=atob(this.toDataURL(type,quality).split(',')[1]),
len=binStr.长度,
arr=新的UINT8阵列(len);
对于(变量i=0;iinputfile.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(...);
});
};
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;
}
}
var minifyImg = function(dataUrl,newWidth,imageType="image/jpeg",resolve,imageArguments=0.7){
var image, oldWidth, oldHeight, newHeight, canvas, ctx, newDataUrl;
(new Promise(function(resolve){
image = new Image(); image.src = dataUrl;
log(image);
resolve('Done : ');
})).then((d)=>{
oldWidth = image.width; oldHeight = image.height;
log([oldWidth,oldHeight]);
newHeight = Math.floor(oldHeight / oldWidth * newWidth);
log(d+' '+newHeight);
canvas = document.createElement("canvas");
canvas.width = newWidth; canvas.height = newHeight;
log(canvas);
ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0, newWidth, newHeight);
//log(ctx);
newDataUrl = canvas.toDataURL(imageType, imageArguments);
resolve(newDataUrl);
});
};
minifyImg(<--DATAURL_HERE-->,<--new width-->,<--type like image/jpeg-->,(data)=>{
console.log(data); // the new DATAURL
});
<!DOCTYPE html>
<html>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
Select image to upload:
<input type="file" name="fileToUpload" id="fileToUpload" multiple>
<input type="submit" value="Upload Image" name="submit">
</form>
</body>
</html>
function upload(){
var f = fileToUpload.files[0];
var fileName = f.name.split('.')[0];
var img = new Image();
img.src = URL.createObjectURL(f);
img.onload = function(){
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
canvas.toBlob(function(blob){
console.info(blob.size);
var f2 = new File([blob], fileName + ".jpeg");
var xhr = new XMLHttpRequest();
var form = new FormData();
form.append("fileToUpload", f2);
xhr.open("POST", "upload.php");
xhr.send(form);
}, 'image/jpeg', 0.5);
}
}
|0.9| 777KB |
|0.8| 383KB |
|0.7| 301KB |
|0.6| 251KB |
|0.5| 219kB |