197 Stimmen

HTML5 Bilder vor dem Hochladen in der Größe anpassen

Hier ist ein Nudelkratzer.

In Anbetracht der Tatsache, dass wir HTML5 lokale Speicherung und xhr v2 und was nicht haben. Ich frage mich, ob jemand ein funktionierendes Beispiel finden konnte oder sogar nur geben mir ein Ja oder Nein für diese Frage:

Ist es möglich, die Größe eines Bildes mit Hilfe des neuen lokalen Speichers (oder was auch immer) vorzugeben, so dass ein Benutzer, der keine Ahnung von der Größenänderung eines Bildes hat, sein 10 MB großes Bild auf meine Website ziehen kann, es mit Hilfe des neuen lokalen Speichers verkleinert und DANN in der kleineren Größe hochlädt.

Ich weiß sehr wohl, dass man das mit Flash, Java-Applets, Active X... machen kann. Die Frage ist, ob man es mit Javascript + Html5 machen kann.

Ich freue mich auf die Antwort zu diesem Thema.

Danke für den Moment.

196voto

robertc Punkte 72049

Ja, verwenden Sie die Datei-API können Sie die Bilder mit der Funktion Leinwandelement .

Dieser Blogbeitrag von Mozilla Hacks führt Sie durch den Großteil des Prozesses. Als Referenz finden Sie hier 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

117voto

Ross Taylor-Turner Punkte 3689

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

Die Antwort von robertc verwendet die Lösung, die in der Mozilla Hacks Blogbeitrag Ich stellte jedoch fest, dass die Bildqualität sehr schlecht war, wenn die Größe nicht im Maßstab 2:1 (oder einem Vielfachen davon) geändert wurde. Ich begann mit verschiedenen Algorithmen zur Größenänderung von Bildern zu experimentieren, obwohl die meisten am Ende ziemlich langsam waren oder auch keine gute Qualität boten.

Schließlich habe ich eine Lösung gefunden, die meiner Meinung nach schnell ausgeführt werden kann und auch eine ziemlich gute Leistung hat - da die Mozilla-Lösung des Kopierens von einer Leinwand auf eine andere bei einem Verhältnis von 2:1 schnell und ohne Verlust der Bildqualität funktioniert, wenn man ein Ziel von x Pixel breit und y Pixel hoch ist, verwende ich diese Methode zur Größenänderung der Leinwand, bis das Bild zwischen x und 2 x et y und 2 y . An diesem Punkt wende ich mich dann für den letzten "Schritt" der Größenanpassung an die Zielgröße einem Algorithmus zu. Nachdem ich verschiedene Algorithmen ausprobiert hatte, entschied ich mich für die bilineare Interpolation aus einem Blog, der nicht mehr online ist, aber zugänglich über das Internet-Archiv , die gute Ergebnisse liefert, hier 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;
        }
    }
};

Damit wird ein Bild auf eine Breite von config.maxWidth unter Beibehaltung des ursprünglichen Seitenverhältnisses. Zum Zeitpunkt der Entwicklung funktionierte dies auf iPad/iPhone Safari zusätzlich zu den wichtigsten Desktop-Browsern (IE9+, Firefox, Chrome), so dass ich davon ausgehe, dass es angesichts der breiteren Akzeptanz von HTML5 heute immer noch kompatibel sein wird. Beachten Sie, dass der Aufruf canvas.toDataURL() einen Mime-Typ und eine Bildqualität entgegennimmt, die es Ihnen ermöglichen, die Qualität und das Format der Ausgabedatei zu steuern (möglicherweise anders als die Eingabe, wenn Sie es wünschen).

Der einzige Punkt, den dies nicht abdeckt, ist die Beibehaltung der Ausrichtungsinformationen. Ohne Kenntnis dieser Metadaten wird das Bild in der Größe verändert und so gespeichert, wie es ist, wobei alle Metadaten innerhalb des Bildes für die Ausrichtung verloren gehen, was bedeutet, dass Bilder, die auf einem Tablet-Gerät "auf dem Kopf stehend" aufgenommen wurden, auch so wiedergegeben werden, obwohl sie im Kamerasucher des Geräts gespiegelt worden wären. Wenn dies ein Problem ist, dieser Blogbeitrag hat einen guten Leitfaden und Code-Beispiele, wie dies zu erreichen, die ich bin sicher, könnte auf den obigen Code integriert werden.

30voto

Justin Levene Punkte 1505

Berichtigung des obigen Textes:

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

16voto

Will Punkte 1949

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

  1. Img.onload hinzugefügt
  2. Erweitern Sie die POST-Anfrage 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);

    }

9voto

nanospeck Punkte 3088

Wenn Sie das Rad nicht neu erfinden wollen, können Sie versuchen plupload.de

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X