it-swarm.com.de

HTML5 Ändern Sie die Größe von Bildern vor dem Hochladen

Hier ist ein Nudelkratzer.

In Anbetracht dessen haben wir lokalen HTML5-Speicher und xhr v2 und was nicht. Ich habe mich gefragt, ob jemand ein funktionierendes Beispiel finden könnte oder mir einfach nur ein Ja oder Nein für diese Frage geben könnte:

Ist es möglich, ein Bild mithilfe des neuen lokalen Speichers (oder was auch immer) vorab zu formatieren, sodass ein Benutzer, der keine Ahnung von der Bildgröße hat, sein 10-MB-Bild in meine Website ziehen kann, seine Größe mit dem neuen lokalen Speicher und DANN laden Sie es in der kleineren Größe hoch.

Ich weiß genau, dass Sie es mit Flash, Java-Applets, aktivem X tun können ... Die Frage ist, ob Sie mit Javascript + Html5 etwas tun können.

Wir freuen uns auf die Antwort zu diesem Thema.

Ta für jetzt.

170
Jimmyt1988

Ja, verwenden Sie die File API . Dann können Sie die Bilder mit dem canvas-Element bearbeiten.

Dieser Mozilla Hacks-Blogbeitrag führt Sie durch den gesamten Prozess. Hier finden Sie den zusammengestellten Quellcode aus dem Blogbeitrag:

// 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
168
robertc

Ich habe dieses Problem vor einigen Jahren in Angriff genommen und meine Lösung als https://github.com/rossturner/HTML5-ImageUploader in github hochgeladen. _

die Antwort von robertc verwendet die im Mozilla Hacks-Blogpost vorgeschlagene Lösung, jedoch stellte ich fest, dass die Bildqualität wirklich schlecht war, wenn die Größe auf einen Maßstab geändert wurde, der nicht 2: 1 (oder ein Vielfaches davon) war. Ich fing an, mit verschiedenen Algorithmen zur Bildgrößenänderung zu experimentieren, obwohl die meisten am Ende ziemlich langsam waren oder auch nicht in der Qualität waren. 

Schließlich habe ich eine Lösung gefunden, die meines Erachtens schnell ausgeführt wird und auch eine ziemlich gute Leistung zeigt - da die Mozilla-Lösung, von einer Leinwand auf eine andere zu kopieren, schnell und ohne Verlust der Bildqualität im Verhältnis 2: 1 arbeitet, wenn ein Ziel von x Pixel breit und y Pixel hoch, verwende ich diese Methode zur Änderung der Leinwandgröße, bis das Bild zwischen x und 2 x und y und 2 y liegt. An dieser Stelle wende ich mich dann der algorithmischen Bildgröße für den letzten "Schritt" der Größenänderung auf die Zielgröße zu. Nachdem ich verschiedene Algorithmen ausprobiert hatte, entschied ich mich für die bilineare Interpolation eines Blogs, das nicht mehr online ist, sondern über das Internetarchiv zugänglich , was gute Ergebnisse liefert. Hier ist der entsprechende Code:

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

Dadurch wird ein Bild auf eine Breite von config.maxWidth skaliert, wobei das ursprüngliche Seitenverhältnis beibehalten wird. Zum Zeitpunkt der Entwicklung funktionierte dies auf iPad/iPhone Safari zusätzlich zu den wichtigsten Desktop-Browsern (IE9 +, Firefox, Chrome). Ich gehe davon aus, dass es angesichts der heutigen Verbreitung von HTML5 noch kompatibel sein wird. Beachten Sie, dass für den canvas.toDataURL () - Aufruf ein MIME-Typ und eine Bildqualität erforderlich sind, mit denen Sie die Qualität und das Ausgabedateiformat steuern können (ggf. abweichend von der Eingabe).

Der einzige Punkt, auf den dies nicht eingeht, ist die Aufrechterhaltung der Orientierungsinformationen. Ohne Kenntnis dieser Metadaten wird das Bild in seiner Größe geändert und gespeichert, wobei alle Metadaten im Bild zur Orientierung verloren gehen, was bedeutet, dass Bilder, die auf einem Tablet-Gerät aufgenommen wurden, auf dem Kopf standen gerendert werden, obwohl sie im Kamerasucher des Geräts umgedreht worden wären. Wenn dies ein Problem ist, hat dieser Blogbeitrag einen guten Leitfaden und Codebeispiele, wie dies erreicht werden kann. Ich bin sicher, dass er in den obigen Code integriert werden könnte.

101

Korrektur nach oben:

<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>
24
Justin Levene

Änderung an der Antwort von Justin das funktioniert für mich:

  1. Img.onload hinzugefügt
  2. Erweitern Sie die Anforderung POST um ein reales Beispiel

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);
}
13
Will

Wenn Sie das Rad nicht neu erfinden möchten, versuchen Sie plupload.com

8
nanospeck
fd.append("image", dataurl);

Das wird nicht funktionieren. Auf der Seite PHP können Sie keine Datei speichern.

Verwenden Sie stattdessen diesen Code:

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
robot

Die akzeptierte Antwort funktioniert gut, aber die Größenänderungslogik ignoriert den Fall, in dem das Bild nur in einer der Achsen größer als das Maximum ist (z. B. height> maxHeight, width <= maxWidth).

Ich denke, dass der folgende Code alle Fälle auf eine einfachere und funktionellere Weise erledigt (ignorieren Sie die TypeScript-Typanmerkungen, wenn Sie einfaches Javascript verwenden):

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
lautaro.dragan

Das Ändern der Größe von Bildern in einem Canvas-Element ist im Allgemeinen eine schlechte Idee, da die billigste Rahmeninterpolation verwendet wird. Das sich ergebende Bild nimmt in der Qualität ab. Ich empfehle die Verwendung von http://nodeca.github.io/pica/demo/ , die stattdessen eine Lanczos-Transformation durchführen kann. Die obige Demoseite zeigt den Unterschied zwischen Canvas- und Lanczos-Ansätzen.

Außerdem werden Web-Worker zum parallelen Ändern der Bildgröße verwendet. Es gibt auch eine WEBGL-Implementierung.

Es gibt einige Online-Bild-Resizer, die Pica für die Arbeit verwenden, wie https://myimageresizer.com

3
Ivan Nikitin

Typoskript

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;
}
2
jales cardoso

Sie können dropzone.js verwenden, wenn Sie den einfachen und einfachen Upload-Manager mit der Größenänderung vor dem Hochladen verwenden möchten.

Es hat eingebaute Größenänderungsfunktionen, aber Sie können Ihre eigenen zur Verfügung stellen, wenn Sie möchten.

0