Pré-redimensionner les images HTML5 avant de les télécharger

voici un grattoir à nouilles.

en gardant à l'esprit que nous avons un stockage local HTML5 et xhr v2 et ce qui ne l'est pas. Je me demandais si quelqu'un pourrait trouver un exemple de travail ou même juste me donner un oui ou non pour cette question:

est - il possible de prédimensionner une image en utilisant le nouveau stockage local (ou autre), de sorte qu'un utilisateur qui n'a pas la moindre idée de redimensionner une image peut faire glisser son image 10mb dans mon site web, il le redimensionne en utilisant le nouveau stockage local et puis le télécharger à la plus petite taille.

je sais très bien que vous pouvez le faire avec Flash, Java applets, active X... La question Est de savoir si vous pouvez faire avec Javascript + Html5.

attend avec impatience la réponse sur celle-ci.

Ta pour l'instant.

163
demandé sur Jimmyt1988 2012-04-26 17:01:44

10 réponses

Oui, utilisez L'API de fichier , puis vous pouvez traiter les images avec le élément de toile .

ce billet sur le Blog de Mozilla Hacks vous montre la plupart du processus. Pour référence voici le code source assemblé de l'article de blog:

// 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
158
répondu robertc 2016-10-25 11:40:51

j'ai abordé ce problème il ya quelques années et téléchargé ma solution à github en tant que https://github.com/rossturner/HTML5-ImageUploader

la réponse de robertc utilise la solution proposée dans le Mozilla Hacks blog post , mais j'ai trouvé que cela donnait une très mauvaise qualité d'image lors du redimensionnement à une échelle qui n'était pas 2:1 (ou un multiple de celui-ci). J'ai commencé à expérimenter avec différents algorithmes de redimensionnement d'image, bien que la plupart fini par être assez lent, ou autre, n'étaient pas de grande qualité.

finalement j'ai trouvé une solution qui, je crois, s'exécute rapidement et a aussi de très bonnes performances - comme la solution Mozilla de copier de 1 toile à une autre travaille rapidement et sans perte de qualité d'image à un rapport de 2: 1, compte tenu d'une cible de x pixels de large et y pixels de haut, j'utilise cette méthode de redimensionnement de toile jusqu'à ce que l'image soit entre x et 2 x , et y et 2 y . À ce point, je me tourne alors vers le redimensionnement d'image algorithmique pour la dernière "étape" de redimensionnement à la taille cible. Après avoir essayé plusieurs algorithmes différents, j'ai réglé sur l'interpolation bilinear pris d'un blog qui n'est plus en ligne mais accessible via L'Archive Internet , qui donne de bons résultats, voici le code applicable:

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

cette échelle une image vers le bas à une largeur de config.maxWidth , en maintenant le rapport d'aspect original. Au moment du développement, cela a fonctionné sur iPad/iPhone Safari en plus des principaux navigateurs de bureau (IE9+, Firefox, Chrome) donc je m'attends à ce qu'il sera toujours compatible étant donné l'adoption plus large de HTML5 aujourd'hui. Notez que la zone de travail.toDataURL () call prend un type mime et une qualité d'image qui vous permettra de contrôler la qualité et le format de fichier de sortie (potentiellement différents à l'entrée si vous le désirez).

le seul point que cela ne couvre pas est de maintenir l'information d'orientation, sans connaissance de ces métadonnées, l'image est redimensionnée et sauvegardée telle quelle, perdant toute métadonnée dans l'image pour l'orientation, ce qui signifie que les images prises sur un dispositif de tablette" à l'envers " ont été rendues comme telles, bien qu'elles auraient été retournées dans le viseur de l'appareil. Si cela est une préoccupation, ce billet de blog a un bon guide et exemples de code sur la façon d'accomplir ceci, qui je suis sûr pourrait être intégré au code ci-dessus.

98
répondu Ross Taylor-Turner 2015-04-25 11:54:01

Correction ci-dessus:

<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>
23
répondu Justin Levene 2014-07-15 11:21:33

Modification de la répondre par Justin qui fonctionne pour moi:

  1. ajouté img.onload
  2. étendre la requête POST avec un exemple réel

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);
}
9
répondu Will 2018-02-14 18:41:51

si vous ne voulez pas réinventer la roue vous pouvez essayer plupload.com

8
répondu nanospeck 2012-12-15 05:33:49
fd.append("image", dataurl);

ça ne marchera pas. Du côté de PHP, vous ne pouvez pas sauvegarder le fichier avec ceci.

utilisez ce code à la place:

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
5
répondu robot 2016-03-09 16:18:45

la réponse acceptée fonctionne très bien, mais la logique de redimensionnement ignore le cas dans lequel l'image est plus grande que le maximum dans un seul des axes (par exemple, hauteur > maxHeight mais largeur <= maxWidth).

je pense que le code suivant s'occupe de tous les cas d'une manière plus directe et fonctionnelle (ignorez les annotations de type dactylographié si vous utilisez du javascript simple):

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
répondu lautaro.dragan 2017-07-27 01:47:24

redimensionner des images dans un élément de toile est généralement une mauvaise idée car il utilise la moins chère boîte d'interpolation interpolation. L'image résultante se dégrade de façon perceptible dans la qualité. Je recommande d'utiliser http://nodeca.github.io/pica/demo / qui peut effectuer la transformation Lanczos à la place. La page de démonstration ci-dessus montre la différence entre les approches de canvas et de Lanczos.

il utilise également des travailleurs web pour redimensionner des images en parallèle. Il y a aussi la mise en œuvre de WEBGL.

il y a des Redimensionneurs d'image en ligne qui utilisent pica pour faire le travail, comme https://myimageresizer.com

2
répondu Ivan Nikitin 2017-04-16 19:50:18

vous pouvez utiliser dropzone.js si vous voulez utiliser Gestionnaire de téléchargement simple et facile avec redimensionnement avant de télécharger des fonctions.

il a intégré des fonctions de redimensionnement, mais vous pouvez fournir votre propre si vous voulez.

0
répondu Michał Zalewski 2018-03-15 12:55:09

Tapuscrit

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;
}
0
répondu jalescardoso 2018-08-17 22:18:57