如何在浏览器中通过Javascript压缩图像?

时间:2021-06-15 21:23:09

TL;DR;

Is there a way to compress an image (mostly jpeg, png and gif) directly browser-side, before uploading it ? I'm pretty sure JavaScript can do this, but I can't find a way to achieve it.

有没有办法在上传之前直接在浏览器端压缩图像(主要是jpeg,png和gif)?我很确定JavaScript可以做到这一点,但我找不到实现它的方法。


Here's the full scenario I would like to implement:

这是我想要实现的完整场景:

  • the user goes to my website, and choose an image via an input type="file" element,
  • 用户进入我的网站,并通过input type =“file”元素选择图像,

  • this image is retrieved via JavaScript, we do some verification such as correct file format, maximum file size etc,
  • 这个图像是通过JavaScript检索的,我们做了一些验证,如正确的文件格式,最大文件大小等,

  • if every thing is OK, a preview of the image is displayed on the page,
  • 如果一切正常,页面上会显示图像的预览,

  • the user can do some basic operations such as rotate the image by 90°/-90°, crop it following a pre-defined ratio, etc, or the user can upload another image and return to step 1,
  • 用户可以进行一些基本操作,例如将图像旋转90°/ -90°,按照预定义的比例裁剪等,或者用户可以上传另一张图像并返回步骤1,

  • when the user is satisfied, the edited image is then compressed and "saved" locally (not saved to a file, but in the browser memory/page),-
  • 当用户满意时,编辑后的图像会被压缩并在本地“保存”(不保存到文件中,但在浏览器内存/页面中), -

  • the user fill a form with data like name, age etc,
  • 用户填写表格,其中包含姓名,年龄等数据,

  • the user click on the "Finish" button, then the form containing datas + compressed image is sent to the server (without AJAX),
  • 用户单击“完成”按钮,然后将包含数据+压缩图像的表单发送到服务器(不带AJAX),

The full process up to the last step should be done client side, and should be compatible on latest Chrome and Firefox, Safari 5+ and IE 8+. If possible, only JavaScript should be used (but I'm pretty sure this is not possible).

直到最后一步的完整过程应该在客户端完成,并且应该与最新的Chrome和Firefox,Safari 5+和IE 8+兼容。如果可能,只应使用JavaScript(但我很确定这是不可能的)。

I've not code anything right now, but I've thought about it already. File reading locally is possible via File API, image previewing and editing could be done using Canvas element, but I can't find a way to do the image compression part.

我现在没有编码任何东西,但我已经考虑过了。可以通过File API在本地读取文件,可以使用Canvas元素完成图像预览和编辑,但我找不到一种方法来执行图像压缩部分。

According to html5please.com and caniuse.com, supporting those browser is quite hard (thanks to IE), but could be done using polyfill such as FlashCanvas and FileReader.

根据html5please.com和caniuse.com,支持这些浏览器非常困难(感谢IE),但可以使用FlashCanvas和FileReader之类的polyfill来完成。

Actually, the goal is to reduce file size, so I see image compression as a solution. But, I know that uploaded images are going to be displayed on my website, every time at the same place, and I know the dimension of this display area (eg. 200x400). So, I could resize the image to fit those dimensions, thus reducing file size. I have no idea what would be the compression ratio for this technique.

实际上,目标是减小文件大小,因此我将图像压缩视为一种解决方案。但是,我知道上传的图像将在我的网站上显示,每次都在同一个地方,我知道这个显示区域的尺寸(例如200x400)。因此,我可以调整图像大小以适应这些尺寸,从而减小文件大小。我不知道这种技术的压缩比是多少。

What do you think ? Do you have any advice to tell me ? Do you know any way to compress an image browser-side in JavaScript ? Thanks for your replies.

你怎么看 ?你有什么建议告诉我吗?你知道在JavaScript中压缩图像浏览器的方法吗?谢谢你的回复。

6 个解决方案

#1


100  

In short:

  • Read the files using the HTML5 FileReader API with .readAsArrayBuffer
  • 使用带有.readAsArrayBuffer的HTML5 FileReader API读取文件

  • Create e Blob with the file data and get its url with window.URL.createObjectURL(blob)
  • 使用文件数据创建e Blob并使用window.URL.createObjectURL(blob)获取其URL

  • Create new Image element and set it's src to the file blob url
  • 创建新的Image元素并将其src设置为文件blob url

  • Send the image to the canvas. The canvas size is set to desired output size
  • 将图像发送到画布。画布大小设置为所需的输出大小

  • Get the scaled-down data back from canvas via canvas.toDataURL("image/jpeg",0.7) (set your own output format and quality)
  • 通过canvas.toDataURL(“image / jpeg”,0.7)从画布返回缩小的数据(设置自己的输出格式和质量)

  • Attach new hidden inputs to the original form and transfer the dataURI images basically as normal text
  • 将新的隐藏输入附加到原始表单,并将dataURI图像基本上作为普通文本传输

  • On backend, read the dataURI, decode from Base64, and save it
  • 在后端,读取dataURI,从Base64解码,然后保存

Source: code.

#2


9  

@PsychoWoods' answer is good. I would like to offer my own solution. This Javascript function takes an image data URL and a width, scales it to the new width, and returns a new data URL.

@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;}

This code can be used anywhere you have a data URL and want a data URL for a downscaled image.

此代码可以在您拥有数据URL的任何地方使用,并且需要缩小图像的数据URL。

#3


8  

I see two things missing from the other answers:

我看到其他答案中缺少两件事:

  • canvas.toBlob (when available) is more performant than canvas.toDataURL, and also async.
  • canvas.toBlob(如果可用)比canvas.toDataURL更高效,也是异步。

  • the file -> image -> canvas -> file conversion loses EXIF data; in particular, data about image rotation commonly set by modern phones/tablets.
  • 文件 - >图像 - >画布 - >文件转换丢失EXIF数据;特别是,关于图像旋转的数据通常由现代手机/平板电脑设定。

The following script deals with both points:

以下脚本处理这两点:

// 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://*.com/a/32490603, cc by-sa 3.0// -2 = not jpeg, -1 = no data, 1..8 = orientationsfunction 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://*.com/a/40867559, cc by-safunction 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);}

Example usage:

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(...);    });};

#4


3  

Edit: As per the Mr Me comment on this answer, it looks like compression is now available for JPG/WebP formats ( see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL ).

编辑:根据Mr Me对此答案的评论,看起来压缩现在可用于JPG / WebP格式(请参阅https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL) 。

As far as I know, you cannot compress images using canvas, instead, you can resize it. Using canvas.toDataURL will not let you choose the compression ratio to use. You can take a look at canimage that does exactly what you want : https://github.com/nfroidure/CanImage/blob/master/chrome/canimage/content/canimage.js

据我所知,你无法使用画布压缩图像,相反,你可以调整它的大小。使用canvas.toDataURL不会让您选择要使用的压缩率。您可以查看完全符合您要求的canimage:https://github.com/nfroidure/CanImage/blob/master/chrome/canimage/content/canimage.js

In fact, it's often sufficient to just resize the image to decrease it's size but if you want to go further, you'll have to use newly introduced method file.readAsArrayBuffer to get a buffer containing the image data.

实际上,通常只需调整图像大小以减小其大小即可,但如果您想要更进一步,则必须使用新引入的方法file.readAsArrayBuffer来获取包含图像数据的缓冲区。

Then, just use a DataView to read it's content according to the image format specification (http://en.wikipedia.org/wiki/JPEG or http://en.wikipedia.org/wiki/Portable_Network_Graphics).

然后,根据图像格式规范(http://en.wikipedia.org/wiki/JPEG或http://en.wikipedia.org/wiki/Portable_Network_Graphics)使用DataView读取内容。

It'll be hard to deal with image data compression, but it is worse a try. On the other hand, you can try to delete the PNG headers or the JPEG exif data to make your image smaller, it should be easier to do so.

处理图像数据压缩会很困难,但尝试更糟糕。另一方面,您可以尝试删除PNG标头或JPEG exif数据以使图像更小,这样做应该更容易。

You'll have to create another DataWiew on another buffer and fill it with the filtered image content. Then, you'll just have to encode you're image content to DataURI using window.btoa.

您必须在另一个缓冲区上创建另一个DataWiew,并用过滤后的图像内容填充它。然后,您只需使用window.btoa将图像内容编码为DataURI。

Let me know if you implement something similar, will be interesting to go through the code.

让我知道如果你实现类似的东西,将通过代码将是有趣的。

#5


1  

I had an issue with the downscaleImage() function posted above by @daniel-allen-langdon in that the image.width and image.height properties are not available immediately because the image load is asynchronous.

上面由@ daniel-allen-langdon发布的downscaleImage()函数存在一个问题,因为图像加载是异步的,因此image.width和image.height属性不能立即使用。

Please see updated TypeScript example below that takes this into account, uses async functions, and resizes the image based on the longest dimension rather than just the width

请参阅下面更新的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;    }}

#6


0  

For JPG Image compression you can use the best compression technique called JIC(Javascript Image Compression)This will definitely help you -->https://github.com/brunobar79/J-I-C

对于JPG图像压缩,您可以使用称为JIC(Javascript图像压缩)的最佳压缩技术。这肯定会对您有帮助 - > https://github.com/brunobar79/J-I-C

#1


100  

In short:

  • Read the files using the HTML5 FileReader API with .readAsArrayBuffer
  • 使用带有.readAsArrayBuffer的HTML5 FileReader API读取文件

  • Create e Blob with the file data and get its url with window.URL.createObjectURL(blob)
  • 使用文件数据创建e Blob并使用window.URL.createObjectURL(blob)获取其URL

  • Create new Image element and set it's src to the file blob url
  • 创建新的Image元素并将其src设置为文件blob url

  • Send the image to the canvas. The canvas size is set to desired output size
  • 将图像发送到画布。画布大小设置为所需的输出大小

  • Get the scaled-down data back from canvas via canvas.toDataURL("image/jpeg",0.7) (set your own output format and quality)
  • 通过canvas.toDataURL(“image / jpeg”,0.7)从画布返回缩小的数据(设置自己的输出格式和质量)

  • Attach new hidden inputs to the original form and transfer the dataURI images basically as normal text
  • 将新的隐藏输入附加到原始表单,并将dataURI图像基本上作为普通文本传输

  • On backend, read the dataURI, decode from Base64, and save it
  • 在后端,读取dataURI,从Base64解码,然后保存

Source: code.

#2


9  

@PsychoWoods' answer is good. I would like to offer my own solution. This Javascript function takes an image data URL and a width, scales it to the new width, and returns a new data URL.

@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;}

This code can be used anywhere you have a data URL and want a data URL for a downscaled image.

此代码可以在您拥有数据URL的任何地方使用,并且需要缩小图像的数据URL。

#3


8  

I see two things missing from the other answers:

我看到其他答案中缺少两件事:

  • canvas.toBlob (when available) is more performant than canvas.toDataURL, and also async.
  • canvas.toBlob(如果可用)比canvas.toDataURL更高效,也是异步。

  • the file -> image -> canvas -> file conversion loses EXIF data; in particular, data about image rotation commonly set by modern phones/tablets.
  • 文件 - >图像 - >画布 - >文件转换丢失EXIF数据;特别是,关于图像旋转的数据通常由现代手机/平板电脑设定。

The following script deals with both points:

以下脚本处理这两点:

// 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://*.com/a/32490603, cc by-sa 3.0// -2 = not jpeg, -1 = no data, 1..8 = orientationsfunction 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://*.com/a/40867559, cc by-safunction 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);}

Example usage:

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(...);    });};

#4


3  

Edit: As per the Mr Me comment on this answer, it looks like compression is now available for JPG/WebP formats ( see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL ).

编辑:根据Mr Me对此答案的评论,看起来压缩现在可用于JPG / WebP格式(请参阅https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL) 。

As far as I know, you cannot compress images using canvas, instead, you can resize it. Using canvas.toDataURL will not let you choose the compression ratio to use. You can take a look at canimage that does exactly what you want : https://github.com/nfroidure/CanImage/blob/master/chrome/canimage/content/canimage.js

据我所知,你无法使用画布压缩图像,相反,你可以调整它的大小。使用canvas.toDataURL不会让您选择要使用的压缩率。您可以查看完全符合您要求的canimage:https://github.com/nfroidure/CanImage/blob/master/chrome/canimage/content/canimage.js

In fact, it's often sufficient to just resize the image to decrease it's size but if you want to go further, you'll have to use newly introduced method file.readAsArrayBuffer to get a buffer containing the image data.

实际上,通常只需调整图像大小以减小其大小即可,但如果您想要更进一步,则必须使用新引入的方法file.readAsArrayBuffer来获取包含图像数据的缓冲区。

Then, just use a DataView to read it's content according to the image format specification (http://en.wikipedia.org/wiki/JPEG or http://en.wikipedia.org/wiki/Portable_Network_Graphics).

然后,根据图像格式规范(http://en.wikipedia.org/wiki/JPEG或http://en.wikipedia.org/wiki/Portable_Network_Graphics)使用DataView读取内容。

It'll be hard to deal with image data compression, but it is worse a try. On the other hand, you can try to delete the PNG headers or the JPEG exif data to make your image smaller, it should be easier to do so.

处理图像数据压缩会很困难,但尝试更糟糕。另一方面,您可以尝试删除PNG标头或JPEG exif数据以使图像更小,这样做应该更容易。

You'll have to create another DataWiew on another buffer and fill it with the filtered image content. Then, you'll just have to encode you're image content to DataURI using window.btoa.

您必须在另一个缓冲区上创建另一个DataWiew,并用过滤后的图像内容填充它。然后,您只需使用window.btoa将图像内容编码为DataURI。

Let me know if you implement something similar, will be interesting to go through the code.

让我知道如果你实现类似的东西,将通过代码将是有趣的。

#5


1  

I had an issue with the downscaleImage() function posted above by @daniel-allen-langdon in that the image.width and image.height properties are not available immediately because the image load is asynchronous.

上面由@ daniel-allen-langdon发布的downscaleImage()函数存在一个问题,因为图像加载是异步的,因此image.width和image.height属性不能立即使用。

Please see updated TypeScript example below that takes this into account, uses async functions, and resizes the image based on the longest dimension rather than just the width

请参阅下面更新的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;    }}

#6


0  

For JPG Image compression you can use the best compression technique called JIC(Javascript Image Compression)This will definitely help you -->https://github.com/brunobar79/J-I-C

对于JPG图像压缩,您可以使用称为JIC(Javascript图像压缩)的最佳压缩技术。这肯定会对您有帮助 - > https://github.com/brunobar79/J-I-C