首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在浏览器中通过Javascript压缩图像?

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

Stack Overflow用户
提问于 2013-02-03 13:02:24
回答 13查看 198.5K关注 0票数 156

TL;DR;

在上传图像之前,是否有一种直接压缩图像(主要是jpeg、png和gif)的方法?我确信JavaScript可以做到这一点,但我无法找到实现它的方法。

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

  • 用户转到我的网站,通过input type="file"元素选择图片,
  • 这个图像是通过JavaScript检索的,我们做了一些验证,如正确的文件格式,最大文件大小等,
  • 如果一切正常,则在页面上显示图像的预览,
  • 用户可以做一些基本操作,如将图像旋转90°/-90°,按照预定的比例裁剪图像等,或者用户可以上传另一张图像并返回到步骤1,
  • 当用户满意时,编辑的图像将被压缩并在本地“保存”(不是保存到文件中,而是在浏览器内存/页中),
  • 用户填写的表格中有姓名、年龄等数据,
  • 用户单击"Finish“按钮,然后将包含数据+压缩图像的表单发送到服务器(没有AJAX),

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

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

根据html5please.comcaniuse.com的说法,支持这些浏览器是相当困难的(感谢IE),但是可以使用FlashCanvasFileReader这样的多边形填充。

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

你觉得呢?你有什么建议要告诉我吗?您知道在JavaScript中压缩图像浏览器端的方法吗?谢谢你的回复。

EN

回答 13

Stack Overflow用户

回答已采纳

发布于 2013-02-03 13:24:11

简言之:

  • 使用HTML5 FileReader API和.readAsArrayBuffer读取文件
  • 使用文件数据创建一个Blob,并使用window.URL.createObjectURL(blob)获取它的url
  • 创建新的Image元素并将它的src设置为文件blob url
  • 将图像发送到画布上。画布大小设置为所需的输出大小。
  • 通过canvas.toDataURL("image/jpeg",0.7)从画布中获取缩小的数据(设置您自己的输出格式和质量)
  • 将新的隐藏输入附加到原始表单上,并以正常文本的形式传输dataURI图像。
  • 在后端,读取dataURI,从Base64解码并保存它

资料来源:代码.

票数 232
EN

Stack Overflow用户

发布于 2017-06-30 14:46:34

我发现其他答案中遗漏了两件事:

  • canvas.toBlob (在可用的情况下)比canvas.toDataURL具有更高的性能,而且还具有异步性。
  • 文件->图像->画布->文件转换会丢失EXIF数据,特别是现代手机/平板电脑通常设置的图像旋转数据。

以下脚本处理这两点:

代码语言:javascript
运行
复制
// 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);
}

示例用法:

代码语言:javascript
运行
复制
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(...);
    });
};
票数 40
EN

Stack Overflow用户

发布于 2016-09-27 22:31:48

@精神科伍兹的答案是好的。我想提出我自己的解决办法。这个Javascript函数接受一个图像数据URL和一个宽度,将其缩放到新的宽度,并返回一个新的数据URL。

代码语言:javascript
运行
复制
// 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的地方。

票数 20
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14672746

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档