Изменение размера изображения перед загрузкой - преобразование холста в объект File

Здесь фрагмент кода, который я использую сейчас, для загрузки нескольких изображений с использованием API файлов HTML5:

/**
 * @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);

    }
}

Я хотел бы изменить размер изображений перед загрузкой с использованием объекта canvas, но не имея опыта с этим, я не уверен, как можно обновить код с помощью методов, например. описанный здесь: HTML5 Предварительный просмотр изображений перед загрузкой

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

Попробуйте что-то вроде этого:

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://stackoverflow.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 не будет работать. Вам нужно дождаться загрузки, прежде чем вы сможете использовать загруженные данные и отключить вызов вашей функции загрузки для файла EACH.

Кроме того, у файлового загрузчика могут быть проблемы с совместимостью с браузером, но я не думаю, что это возможно на любой другой стороне клиента.

Ответ 2

Изменение размера клиентской стороны

Вариант 1. Используйте библиотеку Pica (https://github.com/nodeca/pica)

Вариант 2. Используйте следующий пользовательский script (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);

Ответ 3

Вы можете вызвать toBlob в элементе <canvas>. Это вернет Blob, который является родительским интерфейсом File. Затем вы можете отправить этот объект на свой сервер через XHR2.