Bildverarbeitung mit der Javascript File API und Data-URLs - im Browser und ohne Server

Vor einiger Zeit habe ich mich mit den Möglichkeiten auseinandergesetzt, Bilddateien ohne Kommunikation mit dem Server mit Javascript zu verarbeiten. Herausgekommen ist ein kleiner Prototyp, den ich hier vorstellen möchte.

Idee

Der Prototyp stellt mehrere Tabs dar. Im ersten wählt man die Ursprungsbilddatei aus.

clientseitige_bildtransformation_dateiauswahl

In den weiteren Tabs wird das transformierte Bild sowie die Data-URL (dazu später mehr) dargestellt.

clientseitige_bildtransformation_black_white

Prototyp in Github und Livedemo

Der Sourcecode ist verfügbar unter: https://github.com/bjoerne2/js-image-prototype/

Den Prototypen habe ich auf meinem Webspace deployt: https://www.bjoerne.com/development/js-image-prototype/

Verwendete Bibliotheken

Der Prototyp ist mit AngularJS umgesetzt. Damit das ganze nett aussieht, kommt Bootstrap zum Einsatz, sowie die Bootstrap-Angular-Bridge von AngularUI.

Da Bootstrap den Dateiupload nicht ausreichend unterstützt, verwende ich den Jasny Boostrap Fork. Boostrap verwendet jQuery, im Code verwende ich allerdings das von AngularJS mitgebrachte jqLite.

Verarbeitung der Dateiauswahl

Wenn der User eine Datei per Datei-Input-Feld auswählt, wird das change-Event gefeuert. Daher muss für die Verarbeitung ein Event-Handler registriert werden.

Üblicherweise unterstützt das AngularJS mit Direktiven, naheliegend wäre hier ein ng-change. Das funktioniert aber nicht, wie hier ausgiebig diskutiert wird. Das Problem liegt darin, dass ng-change ein Model benötigt, die Verwendung eines Model aber automatisch zu Two-Way-Binding führt, welches bei File-Input-Felder nicht funktioniert.

Angular-Direktive für Dateiauswahl

Die Lösung ist eine eigene Direktive, die ich customFileChange genannt habe.

function fileChangeDirective() {
	return {
		restrict : 'A',
		controller : function($parse, $element, $attrs, $scope) {
			var modelExpr = $parse($attrs.customFiles);
			var changeExpr = $parse($attrs.customFileChange);
			$element.bind('change', function() {
				modelExpr.assign($scope, this.files);
				changeExpr($scope);
				$scope.$apply();
			});
		}
	};
}

angular.module('jsip', ['ui.bootstrap']).directive('customFileChange', fileChangeDirective);

Im HTML-Code sieht das wie folgt aus:

<input type="file" name="..." custom-files="files" custom-file-change="selectFile()">

Die Direktive unterstützt zwei Parameter: custom-files und custom-file-change. Ersterer ist der Name unter dem die ausgewählte Datei (als Array) im Scope abgelegt werden, zweiterer der Event-Handler, der angesprochen wird, nachdem eine Datei ausgewählt wurde.

Event-Handler

In klassischen Webanwendungen wird das Datei-Input-Feld dazu verwendet, eine Datei auszuwählen, die beim Absenden eines Formulars an den Server übermittelt wird. Im hier beschriebenen Fall wird gar nicht mit dem Server kommuniziert. Stattdessen übernimmt ein Event-Handler die Verarbeitung. Möglich wurde das durch die File API, die mit HTML5 eingeführt wurde. So sieht der Handler aus:

    $scope.selectFile = function() {
        var f = $scope.files[0];
        var reader;
        if (!f.type.match('image.*')) {
            console.log('No image file');
            return;
        }
        reader = new FileReader();
        reader.onload = function(event) {
            $scope.fileSelected = true;
            $scope.urls = {};
            $scope.urls.plain = event.target.result;
            $scope.$apply();
        };
        reader.readAsDataURL(f);
    };

Durch die Angular-Direktive steht im Scope die ausgewählte Datei zur Verfügung. Der Inhalt wird mit einem FileReader gelesen. Bevor das jedoch passiert, muss am Reader ein onload-Callback für die weitere Verarbeitung registriert werden.

Data-URL: Dateiinhalt als URL

Beim Lesen der Datei wird die Methode readAsDataURL verwendet. Das Ergebnis ist nicht etwa der bytegenaue Inhalt der Datei, sondern die Datei als Data-URL. Eine Data-URL ist eine URL, die nicht auf eine Datei auf dem Server zeigt, sondern den kompletten Dateiinhalt enthält. Sie beginnt immer mit data:, gefolgt von Metainformationen und dem Base64-kodierten Inhalt der Datei. Diese URL wird dann als src des img-Tags im HTML-Code verwendet, hier über die Angular-Direktive ng-src. Mit diesem Vorgehen ist kein Server notwendig.

Transformation des Bildes

Um die Möglichkeit der weiteren Verarbeitung aufzuzeigen, habe ich in drei Tabs jeweils eine transformierte Version des ausgewählten Bildes dargestellt: Eine skalierte Version (halbe Breite/Höhe), eine Schwarz-Weiß-Version sowie eine mit einem gezeichneten Herz und einem Text angereicherte Version. Die Transformation findet mithilfe eines Canvas-Elements statt, dass sich allerdings nicht im DOM-Baum befindet, sondern temporär mit angular.element erzeugt wird. Mehr dazu im Source-Code.

Browserunterstützung

Zwei Faktoren spielen bei der Unterstützung der Browser eine Rolle:

  • Unterstützung der File API
  • Maximallänge der Data-URLs

Welche Browser die File-API unterstützen, ist hier aufbereitet. Der IE unterstützt diese erst ab Version 10.0, einige ältere mobile Browser unterstützen die File-API nicht. Ansonsten sieht es sehr gut aus.

Data-URLs werden von allen aktuellen Browsern unterstützt. Die maximale Länge, die verarbeitet werden kann, unterscheidet sich jedoch. Der Flaschenhals ist erneut der Internet Explorer, vor allem in älteren Versionen (< 10.0).

Ich habe die aktuellen Versionen von Firefox, Chrome, Safari und Chrome auf dem neuesten Android erfolgreich getestet mit Bildern von ca. 1,5 MB und Data-URLs von ca. 2 MB.

Weitere Informationen

Den folgenden Artikel zur Codequalität von Javascript finde ich sehr inspirierend und einige Tipps habe ich bei der Entwicklung des Prototypen versucht zu befolgen:

http://net.tutsplus.com/tutorials/javascript-ajax/the-essentials-of-writing-high-quality-javascript/

 

bjoerne_com_bjoern_weinbrenner_softwareentwickler_icon_leistungen_02

Lust auf mehr? Als Experte für AngularJS und individuelle JavaScript-Entwicklung kann ich Sie in Ihren Projekten unterstützen. Melden Sie sich gerne bei mir.

2017-01-28T14:23:26+00:00 25.12.2013|Tags: , , , |0 Kommentare

Hinterlassen Sie einen Kommentar