上传前调整图像大小 - 将画布转换为文件对象

Posted

技术标签:

【中文标题】上传前调整图像大小 - 将画布转换为文件对象【英文标题】:Resize image before upload - convert canvas to a File object 【发布时间】:2013-04-06 03:17:12 【问题描述】:

这是我现在使用 html5 File API 上传多张图片的代码片段:

/**
 * @param FileList files
 */
upload: function(files)
    nfiles = files.length;

    for (var i = 0; i < nfiles; i++) 
        /** @var file File **/
        var file = files[i];

        var xhr = new XMLHttpRequest();

        xhr.open("POST", settings.post_upload, true);
        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
        xhr.upload.filenumb = i;
        xhr.filenumb = i;
        xhr.upload.filename = file.name;

        var nef = new FormData();
        nef.append("folder", settings.folder);
        nef.append("file_element", settings.file_elem);
        nef.append("udata", settings.user_data);
        nef.append(settings.file_elem, file);
        xhr.send(nef);

    

我想调整图像的大小之前使用画布对象上传,但没有这方面的经验,我不确定如何使用技术更新代码,例如这里描述的那个:HTML5 Pre-resize images before uploading

canvas.toDataURL("image/png"); 将返回一个编码字符串。但我需要发布File 对象。

编辑

您将如何(合理地)为大多数现代浏览器编写跨浏览器功能,以便在上传之前调整文件大小、处理透明的 jpg、png 和 gif:

/** 
 * @param File file 
 * @param int max_width
 * @param int max_height
 * @param float compression_ratio
 * @returns File
 */
function resize(file, max_width, max_height, compression_ratio)

【问题讨论】:

服务器端调整大小有什么问题? +1 关于调整大小需要/必须在客户端进行的原因。 我想他想利用客户端的力量,而不是通过调整图像大小来浪费服务器上的 CPU... @AkashKava 当我大量上传 200 张大图时,可以节省大量传输时间和服务器资源。 客户端调整大小更适合地球。为什么要通过互联网传输大量图像,占用互联网资源?就像每个人都认为互联网依靠神奇的小精灵尘埃运行,但事实并非如此,它依靠能源运行并导致二氧化碳排放。 googleblog.blogspot.com/2009/05/energy-and-internet.html。下次编写程序来调整图像大小时,请考虑一下这个星球。 【参考方案1】:

您可以在&lt;canvas&gt; 元素上调用toBlob。这将返回一个Blob,它是File 的父接口。然后,您可以通过 XHR2 将此对象发送到您的服务器。

【讨论】:

是的,我在这里找到了类似的答案:***.com/questions/4998908/… 但我不确定最佳解决方案(浏览器支持) 您有什么要求?您的具体问题是什么?【参考方案2】:

试试这样的:

function resize(file, max_width, max_height, compression_ratio, imageEncoding)
    var fileLoader = new FileReader(),
    canvas = document.createElement('canvas'),
    context = null,
    imageObj = new Image(),
    blob = null;            

    //create a hidden canvas object we can use to create the new resized image data
    canvas.id     = "hiddenCanvas";
    canvas.width  = max_width;
    canvas.height = max_height;
    canvas.style.visibility   = "hidden";   
    document.body.appendChild(canvas);  

    //get the context to use 
    context = canvas.getContext('2d');  

    // check for an image then
    //trigger the file loader to get the data from the image         
    if (file.type.match('image.*')) 
        fileLoader.readAsDataURL(file);
     else 
        alert('File is not an image');
    

    // setup the file loader onload function
    // once the file loader has the data it passes it to the 
    // image object which, once the image has loaded, 
    // triggers the images onload function
    fileLoader.onload = function() 
        var data = this.result; 
        imageObj.src = data;
    ;

    fileLoader.onabort = function() 
        alert("The upload was aborted.");
    ;

    fileLoader.onerror = function() 
        alert("An error occured while reading the file.");
    ;  


    // set up the images onload function which clears the hidden canvas context, 
    // draws the new image then gets the blob data from it
    imageObj.onload = function()   

        // Check for empty images
        if(this.width == 0 || this.height == 0)
            alert('Image is empty');
         else                 

            context.clearRect(0,0,max_width,max_height);
            context.drawImage(imageObj, 0, 0, this.width, this.height, 0, 0, max_width, max_height);


            //dataURItoBlob function available here:
            // http://***.com/questions/12168909/blob-from-dataurl
            // add ')' at the end of this function SO dont allow to update it without a 6 character edit
            blob = dataURItoBlob(canvas.toDataURL(imageEncoding));

            //pass this blob to your upload function
            upload(blob);
               
    ;

    imageObj.onabort = function() 
        alert("Image load was aborted.");
    ;

    imageObj.onerror = function() 
        alert("An error occured while loading image.");
    ;


请注意:

使用文件加载器和加载图像意味着存在一些延迟,因此该函数是异步的,因此试图简单地返回 blob 数据是行不通的。您需要等待加载发生,然后才能使用加载的数据并为每个文件触发对上传函数的调用。

文件加载器也可能存在一些浏览器兼容性问题,但我认为这在客户端的任何其他方式都是不可能的。

【讨论】:

通常这是可行的,但由于这是一个赏金,我会做 2 个笔记。首先,FileReader 确实存在一些可用性问题:IE 感谢您的标记。对不起,我只是新来的,还在学习:) 这并不完美,但帮助我编写了适合我需要的解决方案 = 赏金是你的,谢谢 :) ps。如果有人已经在使用这种技术,我会很高兴听到有关浏览器支持的更新。 @Sfisioza 你的解决方案是什么?我想做类似的事情。【参考方案3】:

客户端图像大小调整

选项 1. 使用 Pica 库 (https://github.com/nodeca/pica)

选项 2. 使用以下自定义脚本 (http://jsfiddle.net/cL3hapL4/2/)

var file = document.getElementById('imageUpload');
var mime = "image/jpeg";
var max_width = 100;
var max_height = 100;
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();

img.onload = function () 

    // Clear canvas, and resize image
    ctx.clearRect(0, 0, max_width, max_height);
    ctx.drawImage(img,
        0, 0, img.width, img.height, // size of image
        0, 0, max_width, max_height // size of canvas
    );

    // Get image from canvas as a dataURI, and convert to a blob object
    var dataURI = canvas.toDataUrl(mime);
    // Note: This function can be replaced with canvas.toBlob(),
    // once supported by browsers.
    // ---
    var image_blob = (function () 

        var binary = atob(dataURI.split(',')[1]);
        var array = [];
        for(var i = 0; i < binary.length; i++) 
            array.push(binary.charCodeAt(i));
        
        return new Blob([new Uint8Array(array)], type: mime);

    )();
    // ---

    // Attach blob-object (contains our image) to form object
    var form = new Form();
    form.append("image", image_blob);

    // Send AJAX request with multi-part data
    var xhr = new XMLHttpRequest();
    xhr.open('POST', '/route', true);
    xhr.send(form);

    // jQuery version
    //$.ajax(
    //    type: "POST",
    //    url: "/route",
    //    data: form,
    //    processData: false,
    //    contentType: false
    //)
    //.done(function (response) 
    //    console.log(response);
    //);

;
img.src = URL.createObjectURL(file);

【讨论】:

以上是关于上传前调整图像大小 - 将画布转换为文件对象的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 GD 调整上传图像的大小并将其转换为 PNG?

将画布大小调整为窗口/用户控件大小

fabric.js 调整画布大小以适应屏幕

Photoshop

将画布转换为 blob 时如何保持图像大小

在 JavaScript 中调整 Base-64 图像的大小而不使用画布