首页 文章

HTML5在上传之前预先调整图像大小

提问于
浏览
169

Here's a noodle scratcher.

Bearing in mind we have HTML5 local storage and xhr v2 and what not. I was wondering if anyone could find a working example or even just give me a yes or no for this question:

是否可以使用新的本地存储(或其他)预先调整图像大小,以便没有关于调整图像大小的线索的用户可以将他们的10mb图像拖到我的网站中,然后使用新的本地存储调整大小 . 然后以较小的尺寸上传它 .

我完全知道你可以用Flash,Java小程序,活动X来做...问题是你是否可以使用Javascript Html5 .

期待对此的回应 .

Ta现在 .

10 回答

  • 1

    接受的答案很有效,但调整大小逻辑忽略了图像大于仅一个轴中的最大值的情况(例如,高度> maxHeight但宽度<= maxWidth) .

    我认为以下代码以更直接和功能性的方式处理所有情况(如果使用普通的javascript,则忽略typescript类型注释):

    private scaleDownSize(width: number, height: number, maxWidth: number, maxHeight: number): {width: number, height: number} {
        if (width <= maxWidth && height <= maxHeight)
          return { width, height };
        else if (width / maxWidth > height / maxHeight)
          return { width: maxWidth, height: height * maxWidth / width};
        else
          return { width: width * maxHeight / height, height: maxHeight };
      }
    
  • 5

    是的,使用File API,然后您可以使用canvas element处理图像 .

    This Mozilla Hacks blog post引导您完成大部分过程 . 这里参考的是博客文章中汇编的源代码:

    // from an input element
    var filesToUpload = input.files;
    var file = filesToUpload[0];
    
    var img = document.createElement("img");
    var reader = new FileReader();  
    reader.onload = function(e) {img.src = e.target.result}
    reader.readAsDataURL(file);
    
    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);
    
    var MAX_WIDTH = 800;
    var MAX_HEIGHT = 600;
    var width = img.width;
    var height = img.height;
    
    if (width > height) {
      if (width > MAX_WIDTH) {
        height *= MAX_WIDTH / width;
        width = MAX_WIDTH;
      }
    } else {
      if (height > MAX_HEIGHT) {
        width *= MAX_HEIGHT / height;
        height = MAX_HEIGHT;
      }
    }
    canvas.width = width;
    canvas.height = height;
    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0, width, height);
    
    var dataurl = canvas.toDataURL("image/png");
    
    //Post dataurl to the server with AJAX
    
  • 5

    几年前我解决了这个问题并将我的解决方案上传到github为https://github.com/rossturner/HTML5-ImageUploader

    robertc的回答使用了Mozilla Hacks blog post中提出的解决方案,但是我发现当调整到不是2:1(或其倍数)的比例时,这给出了非常差的图像质量 . 我开始尝试不同的图像大小调整算法,虽然大多数最终都很慢,或者质量也不高 .

    最后,我提出了一个解决方案,我相信它可以快速执行并且具有相当不错的性能 - 因为Mozilla从1个画布复制到另一个画布的解决方案很快就可以在不损失图像质量的情况下以2:1的比例运行,给定x的目标像素宽和y像素高,我使用此画布调整大小的方法,直到图像介于x和2 x之间,y和2 y . 在这一点上,我然后转向算法图像大小调整大小调整大小到目标大小的最终"step" . 在尝试了几种不同的算法之后,我决定采用不再在线的博客进行双线性插值,但是accessible via the Internet Archive,这给出了很好的结果,这里是适用的代码:

    ImageUploader.prototype.scaleImage = function(img, completionCallback) {
        var canvas = document.createElement('canvas');
        canvas.width = img.width;
        canvas.height = img.height;
        canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
    
        while (canvas.width >= (2 * this.config.maxWidth)) {
            canvas = this.getHalfScaleCanvas(canvas);
        }
    
        if (canvas.width > this.config.maxWidth) {
            canvas = this.scaleCanvasWithAlgorithm(canvas);
        }
    
        var imageData = canvas.toDataURL('image/jpeg', this.config.quality);
        this.performUpload(imageData, completionCallback);
    };
    
    ImageUploader.prototype.scaleCanvasWithAlgorithm = function(canvas) {
        var scaledCanvas = document.createElement('canvas');
    
        var scale = this.config.maxWidth / canvas.width;
    
        scaledCanvas.width = canvas.width * scale;
        scaledCanvas.height = canvas.height * scale;
    
        var srcImgData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
        var destImgData = scaledCanvas.getContext('2d').createImageData(scaledCanvas.width, scaledCanvas.height);
    
        this.applyBilinearInterpolation(srcImgData, destImgData, scale);
    
        scaledCanvas.getContext('2d').putImageData(destImgData, 0, 0);
    
        return scaledCanvas;
    };
    
    ImageUploader.prototype.getHalfScaleCanvas = function(canvas) {
        var halfCanvas = document.createElement('canvas');
        halfCanvas.width = canvas.width / 2;
        halfCanvas.height = canvas.height / 2;
    
        halfCanvas.getContext('2d').drawImage(canvas, 0, 0, halfCanvas.width, halfCanvas.height);
    
        return halfCanvas;
    };
    
    ImageUploader.prototype.applyBilinearInterpolation = function(srcCanvasData, destCanvasData, scale) {
        function inner(f00, f10, f01, f11, x, y) {
            var un_x = 1.0 - x;
            var un_y = 1.0 - y;
            return (f00 * un_x * un_y + f10 * x * un_y + f01 * un_x * y + f11 * x * y);
        }
        var i, j;
        var iyv, iy0, iy1, ixv, ix0, ix1;
        var idxD, idxS00, idxS10, idxS01, idxS11;
        var dx, dy;
        var r, g, b, a;
        for (i = 0; i < destCanvasData.height; ++i) {
            iyv = i / scale;
            iy0 = Math.floor(iyv);
            // Math.ceil can go over bounds
            iy1 = (Math.ceil(iyv) > (srcCanvasData.height - 1) ? (srcCanvasData.height - 1) : Math.ceil(iyv));
            for (j = 0; j < destCanvasData.width; ++j) {
                ixv = j / scale;
                ix0 = Math.floor(ixv);
                // Math.ceil can go over bounds
                ix1 = (Math.ceil(ixv) > (srcCanvasData.width - 1) ? (srcCanvasData.width - 1) : Math.ceil(ixv));
                idxD = (j + destCanvasData.width * i) * 4;
                // matrix to vector indices
                idxS00 = (ix0 + srcCanvasData.width * iy0) * 4;
                idxS10 = (ix1 + srcCanvasData.width * iy0) * 4;
                idxS01 = (ix0 + srcCanvasData.width * iy1) * 4;
                idxS11 = (ix1 + srcCanvasData.width * iy1) * 4;
                // overall coordinates to unit square
                dx = ixv - ix0;
                dy = iyv - iy0;
                // I let the r, g, b, a on purpose for debugging
                r = inner(srcCanvasData.data[idxS00], srcCanvasData.data[idxS10], srcCanvasData.data[idxS01], srcCanvasData.data[idxS11], dx, dy);
                destCanvasData.data[idxD] = r;
    
                g = inner(srcCanvasData.data[idxS00 + 1], srcCanvasData.data[idxS10 + 1], srcCanvasData.data[idxS01 + 1], srcCanvasData.data[idxS11 + 1], dx, dy);
                destCanvasData.data[idxD + 1] = g;
    
                b = inner(srcCanvasData.data[idxS00 + 2], srcCanvasData.data[idxS10 + 2], srcCanvasData.data[idxS01 + 2], srcCanvasData.data[idxS11 + 2], dx, dy);
                destCanvasData.data[idxD + 2] = b;
    
                a = inner(srcCanvasData.data[idxS00 + 3], srcCanvasData.data[idxS10 + 3], srcCanvasData.data[idxS01 + 3], srcCanvasData.data[idxS11 + 3], dx, dy);
                destCanvasData.data[idxD + 3] = a;
            }
        }
    };
    

    这会将图像缩小到 config.maxWidth 的宽度,从而保持原始高宽比 . 在开发时,除了主要的桌面浏览器(IE9,Firefox,Chrome)之外,它还可以在iPad / iPhone Safari上运行,因此我预计它将保持兼容,因为今天更广泛地使用HTML5 . 请注意,canvas.toDataURL()调用采用mime类型和图像质量,这将允许您控制质量和输出文件格式(如果您愿意,可能与输入不同) .

    唯一的一点就是没有相机取景器 . 如果这是一个问题,this blog post有一个很好的指南和代码示例如何实现这一点,我相信我可以将其集成到上面的代码中 .

  • 24

    以上更正:

    <img src="" id="image">
    <input id="input" type="file" onchange="handleFiles()">
    <script>
    
    function handleFiles()
    {
        var filesToUpload = document.getElementById('input').files;
        var file = filesToUpload[0];
    
        // Create an image
        var img = document.createElement("img");
        // Create a file reader
        var reader = new FileReader();
        // Set the image once loaded into file reader
        reader.onload = function(e)
        {
            img.src = e.target.result;
    
            var canvas = document.createElement("canvas");
            //var canvas = $("<canvas>", {"id":"testing"})[0];
            var ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0);
    
            var MAX_WIDTH = 400;
            var MAX_HEIGHT = 300;
            var width = img.width;
            var height = img.height;
    
            if (width > height) {
              if (width > MAX_WIDTH) {
                height *= MAX_WIDTH / width;
                width = MAX_WIDTH;
              }
            } else {
              if (height > MAX_HEIGHT) {
                width *= MAX_HEIGHT / height;
                height = MAX_HEIGHT;
              }
            }
            canvas.width = width;
            canvas.height = height;
            var ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0, width, height);
    
            var dataurl = canvas.toDataURL("image/png");
            document.getElementById('image').src = dataurl;     
        }
        // Load files into file reader
        reader.readAsDataURL(file);
    
    
        // Post the data
        /*
        var fd = new FormData();
        fd.append("name", "some_filename.jpg");
        fd.append("image", dataurl);
        fd.append("info", "lah_de_dah");
        */
    }</script>
    
  • 0

    修改answer by Justin对我有用:

    • 添加了img.onload

    • 用一个真实的例子扩展POST请求

    function handleFiles()
    {
        var dataurl = null;
        var filesToUpload = document.getElementById('photo').files;
        var file = filesToUpload[0];
    
        // Create an image
        var img = document.createElement("img");
        // Create a file reader
        var reader = new FileReader();
        // Set the image once loaded into file reader
        reader.onload = function(e)
        {
            img.src = e.target.result;
    
            img.onload = function () {
                var canvas = document.createElement("canvas");
                var ctx = canvas.getContext("2d");
                ctx.drawImage(img, 0, 0);
    
                var MAX_WIDTH = 800;
                var MAX_HEIGHT = 600;
                var width = img.width;
                var height = img.height;
    
                if (width > height) {
                  if (width > MAX_WIDTH) {
                    height *= MAX_WIDTH / width;
                    width = MAX_WIDTH;
                  }
                } else {
                  if (height > MAX_HEIGHT) {
                    width *= MAX_HEIGHT / height;
                    height = MAX_HEIGHT;
                  }
                }
                canvas.width = width;
                canvas.height = height;
                var ctx = canvas.getContext("2d");
                ctx.drawImage(img, 0, 0, width, height);
    
                dataurl = canvas.toDataURL("image/jpeg");
    
                // Post the data
                var fd = new FormData();
                fd.append("name", "some_filename.jpg");
                fd.append("image", dataurl);
                fd.append("info", "lah_de_dah");
                $.ajax({
                    url: '/ajax_photo',
                    data: fd,
                    cache: false,
                    contentType: false,
                    processData: false,
                    type: 'POST',
                    success: function(data){
                        $('#form_photo')[0].reset();
                        location.reload();
                    }
                });
            } // img.onload
        }
        // Load files into file reader
        reader.readAsDataURL(file);
    }
    
  • 12

    如果您不想重新发明轮子,可以尝试plupload.com

  • 100
    fd.append("image", dataurl);
    

    这不行 . 在PHP方面,你不能用这个保存文件 .

    请改用此代码:

    var blobBin = atob(dataurl.split(',')[1]);
    var array = [];
    for(var i = 0; i < blobBin.length; i++) {
      array.push(blobBin.charCodeAt(i));
    }
    var file = new Blob([new Uint8Array(array)], {type: 'image/png', name: "avatar.png"});
    
    fd.append("image", file); // blob file
    
  • 3

    调整画布元素中的图像大小通常是个坏主意,因为它使用最便宜的盒子插值 . 产生的图像明显降低质量 . 我建议使用http://nodeca.github.io/pica/demo/来代替Lanczos转换 . 上面的演示页面显示了canvas和Lanczos方法之间的区别 .

    它还使用Web worker并行调整图像大小 . 还有WEBGL实现 .

    有一些在线图像缩放器使用异食癖来完成这项工作,例如https://myimageresizer.com

  • 167

    打字稿

    async resizeImg(file: Blob): Promise<Blob> {
        let img = document.createElement("img");
        img.src = await new Promise<any>(resolve => {
            let reader = new FileReader();
            reader.onload = (e: any) => resolve(e.target.result);
            reader.readAsDataURL(file);
        });
        await new Promise(resolve => img.onload = resolve)
        let canvas = document.createElement("canvas");
        let ctx = canvas.getContext("2d");
        ctx.drawImage(img, 0, 0);
        let MAX_WIDTH = 1000;
        let MAX_HEIGHT = 1000;
        let width = img.naturalWidth;
        let height = img.naturalHeight;
        if (width > height) {
            if (width > MAX_WIDTH) {
                height *= MAX_WIDTH / width;
                width = MAX_WIDTH;
            }
        } else {
            if (height > MAX_HEIGHT) {
                width *= MAX_HEIGHT / height;
                height = MAX_HEIGHT;
            }
        }
        canvas.width = width;
        canvas.height = height;
        ctx = canvas.getContext("2d");
        ctx.drawImage(img, 0, 0, width, height);
        let result = await new Promise<Blob>(resolve => { canvas.toBlob(resolve, 'image/jpeg', 0.95); });
        return result;
    }
    
  • 8

    如果要在上传功能之前使用简单且简单的上传管理器并调整大小,则可以使用dropzone.js .

    它具有内置调整大小功能,但您可以根据需要提供自己的功能 .

相关问题