Letzten Dezember hatte ich das Glück, wieder an der reInvent teilnehmen zu dürfen, und hatte tolle fünf Tage mit interessanten Vorträgen, Veranstaltungen, Spielen und natürlich Partys - wir waren schließlich in Las Vegas! Es ist schwierig, das interessanteste Thema der Veranstaltung auszusuchen. Es gab viel Neues zu entdecken und zu lernen, etwa über maschinelles Lernen, IoT, Big Data, Container, Sicherheit, Datenbanken und serverlose Dienste.

Seit ich letztes Jahr meine Leidenschaft für AWS Lambda entdeckt habe, wollte ich unbedingt einige praktische Anwendungsmöglichkeiten in diesem Bereich kennenlernen. Die Auswahl an Anwendungen, die durch Lambda unterstützt werden, ist endlos. Das Thema einer der Veranstaltungen, an denen ich teilgenommen habe, war ein Klassiker: Die automatische Erstellung von Miniaturansichten, sobald ein Bild einem AWS S3 Bucket hinzugefügt wird.

Ich sage "Klassiker", da dies eines der Beispiele der offiziellen Dokumentation ist und die Quellen auf Github unter Amazon Web Services - Labs zur Verfügung stehen (falls Sie diese noch nicht kennen, sollten Sie unbedingt einen Blick darauf werfen).

Dieses Thema brachte mich auf eine tolle Idee: Da Lambda bereits die Verwendung von ImageMagick für einfache Vorgänge in der Bildbearbeitungen kennt, kann ich es mit der Nuxeo Platform integrieren und alle Miniaturansichten mit Lambda erstellen.

Der Anwendungsfall

Nehmen wir an, Sie würden eine Nuxeo Digital Asset Management Anwendung ausführen, in der alle Binärdateien in S3 gespeichert sind, und Sie möchten jedes Mal beim Hochladen eines neuen Bildes eine Bildbearbeitung durchführen. Da die Blobs bereits in S3 sind, möchten Sie diese Bearbeitung auf die Lambdafunktion übertragen und das Ergebnis wieder im gleichen Bucket speichern.

Das Design

Ich möchte das Beispiel von der Erstellung der Miniaturansicht nutzen, es kann jedoch einfach an Ihre Anforderungen angepasst werden. Im Grunde geschieht folgendes: Sobald die Nuxeo Platform ein Bild in einem Bucket speichert, wird eine Lambda-Funktion aufgerufen, um die Miniaturansicht zu erstellen, im gleichen Bucket wieder abzulegen und dann das bestehende Dokument in der Nuxeo Platform mit dieser Information zu aktualisieren. Da sich die Miniaturansicht bereits im Bucket befindet, müssen wir sie natürlich nicht nochmals hochladen, sondern nur auf sie verweisen.

Allerdings gibt es ein offensichtliches Problem in diesem Prozess. Wie können wir verhindern, dass eine Endlosschleife ausgelöst wird, wenn die Lambda-Funktion durch eine s3:ObjectCreated:Put abgerufen wird und die Miniaturansichten wieder dem gleichen Bucket hinzugefügt werden?

Hier ist die Lösung: Mit Amazon S3 können Ordner innerhalb eines Bucket erstellt werden, um Objekte zu gruppieren, und Lambda-Funktionen können so konfiguriert werden, dass sie durch Events in bestimmten Ordnern ausgelöst werden. Um also die Endlosschleife zu verhindern, benötigen wir zwei Ordner im Bucket: einen zum Speichern des Originalbildes und den anderen, um die erstellte Miniaturansicht abzulegen. Natürlich müssen wir sicherstellen, dass die Nuxeo Platform auf die erstellten Miniaturansichten zugreift.

Die Nuxeo Platform speichert die Binärdateien in S3 anhand ihres Digests als Schlüssel (Name) und kann so konfiguriert werden, dass ein bestimmter Ordner des Buckets verwendet wird (mithilfe der Eigenschaft nuxeo.s3storage.bucket_prefix). Unter diesen Voraussetzungen müssen wir die erstellten Miniaturansichten in den Ordner verschieben, der von der Nuxeo Platform verwendet wird. Nennen wir ihn /nuxeo

Wir müssen also Folgendes erstellen:

  1. Eine Lambda-Funktion, die Miniaturansichten in einem neuen Ordner (/thumbnails) erstellt und jedes Mal abruft, wenn ein neues Objekt dem Ordner /nuxeo hinzugefügt wird. Diese Funktion wird mit dem Event "s3:ObjectCreated:Put" abgerufen.

  2. Eine Lambda-Funktion, die die neuen Dateien aus dem Ordner /thumbnails nach /nuxeo verschiebt und einen Vorgang in der Nuxeo Platform auslöst, um die Informationen mit der neu erstellten Miniaturansicht im Originaldokument zu aktualisieren, sobald die neue Datei erfolgreich verschoben wurde. Da die Erstellung des Objekts durch Kopieren einen "s3:ObjectCreated:Copy"-Event auslöst, wird die erste Funktion nicht erneut aufgerufen, um eine Miniaturansicht von der Miniaturansicht zu erstellen.

Struktur des Buckets
Struktur des Buckets

Ablaufdiagramm
Ablaufdiagramm

Die Implementierung

Den Quellcode für dieses Beispiel (mit beiden Lambda-Funktionen und dem Vorgang in Nuxeo) finden Sie auf github. Ich möcht nicht näher auf die Konfiguration eingehen. Bitte denken Sie jedoch daran, dem Anwender, der die Lambda-Funktionen ausführt, die erforderlichen Lese- und Schreibberechtigungen zu geben. Um dies zu tun, können Sie die gleichen Sicherheitsrichtlinien festlegen, die von der Nuxeo Platform für den Zugriff auf den Bucket erforderlich sind (erklärt im Dokument AWS Configuration).

Wir wollen jetzt die Implementierung in die einzelnen Schritte unterteilen (wir gehen davon aus, dass mein Nuxeo Server bereits die Binärdateien im Bucket test-picture-views-with-lambda und Ordner /nuxeo. speichert):

  1. Deaktivierung der ursprünglichen Erstellung von Miniaturansichten in der Nuxeo Platform

    Dies kann über eine einfache Contribution erfolgen, mit der die Listener deaktiviert werden, die die Miniaturansichten eigentlich erstellen:

    <listener name="updateThumbListener" enabled="false"/>
    <listener name="checkBlobUpdate" enabled="false" />
    
  2. Erstellen der Lambda-Funktion generateThumbnails

    Sie können von Grund auf neu beginnen und die Lambda-Funktion durch Auswahl der Vorlage "image-processing-service" erstellen, oder einfach den Code aus generateThumbnails hochladen (vergessen Sie nicht, den Ordner zu zippen und alle bestehenden Knoten-Module hochzuladen, da hierbei mit ImageMagick verwandte Bibliotheken verwendet werden). Konfigurieren Sie ihn so, dass er abgerufen wird, sobald ein neues Objekt durch die Nuxeo Platform erstellt wird:

    generateThumbnails

    Dies ist der interessante Teil der Funktion:

    var digest = crypto.createHash('md5').update(data).digest('hex');
    var dstKey = 'thumbnails/' + digest;
    s3.putObject({
       Bucket: dstBucket,
       Key: dstKey,
       Body: data,
       ContentType: contentType,
       Metadata: {
           originalFileDigest:originalFileDigest
       }
    },
    next);
    

    Dies speichert die neu erstellte Miniaturansicht anhand ihres Digests als Schlüssel und speichert das Digest des Originalbildes, damit wir später das Dokument in der Nuxeo Platform finden können, um seine Miniaturansicht-Informationen zu aktualisieren.

  3. Erstellen der Lambda-Funktion moveThumbnails

    Aktualisieren Sie den Code von moveThumbnails.js: Die Funktion verschiebt (durch Kopieren) die Miniaturansicht zurück in den Ordner /nuxeo und ruft danach einen Vorgang in der Nuxeo Platform auf, um die Informationen in dem Dokument zu aktualisieren.

    moveThumbnails

    var thumbnailDigest = srcKey.substring('thumbnails'.length + 1, srcKey.length);
    var newKey = 'nuxeo/' + thumbnailDigest;
    
    //Copy - Pasting the object won't trigger a 'putObject' event
    s3.copyObject({
       Bucket: dstBucket,
       Key: newKey,
       CopySource: srcBucket + '/' + srcKey
    }, (err, data) => {
       if (err) {
           console.log('Error copying file:' + err);
       } else {
           //get the object to read the original file digest stored in metadata
           s3.getObject({
                   Bucket: dstBucket,
                   Key: newKey
           }, function(err, data) {
                   if (err) console.log(err, err.stack);
                     else
                   updateThumbnails(data.Metadata['originalfiledigest'], thumbnailDigest);
                    });
              }
    });
    
  4. Erstellen des Vorgangs SetThumbnail.java in der Nuxeo Platform

    Sie können das nuxeo-thumbnails-with-lambda jar erstellen und auf dem Server bereitstellen. Es enthält den Vorgang und die Contribution, um die ursprüngliche Erstellung von Miniaturansichten in der Nuxeo Platform zu deaktivieren. Da der S3 Binary Manager erwartet, dass der Digest des Blobs als Objektschlüssel im Bucket verwendet wird, müssen wir das Digest des Originalbildes (um das Dokument in der Nuxeo Platform zu finden) und das Digest der Miniaturansicht weiterleiten. Natürlich laden wir das Blob nicht nochmals hoch, da sich die Datei bereits im Bucket befindet. Wir sagen der Nuxeo Platform lediglich, mithilfe dieses Digests ein neues Blob zu erstellen und als Miniaturansicht festzulegen.

    Wie Sie aus dem oben abgebildeten Code entnehmen können, ist bereits beides erfüllt, da wir das Digest des Originalbildes als S3 benutzerdefinierte Metadaten auf der erstellen Miniaturansicht festgelegt haben.

    Metadaten

    DocumentModelList docs = session.query(String.format("Select * from Document where content/data = '%s' ",originalFileDigest))
       for (DocumentModel doc : docs) {
           doc.addFacet("Thumbnail");
           BinaryBlob sb = new BinaryBlob(new LazyBinary(thumbnailDigest, "default", getCachingBinaryManager()),
                   thumbnailDigest, (String) doc.getPropertyValue("file:content/name"), "image/png", null,
                   thumbnailDigest, -1);
           doc.setPropertyValue("thumb:thumbnail", sb);
           session.saveDocument(doc);
             }
    

    Das war's dann schon. Werfen wir einen Blick auf ein konkretes Beispiel.

Sehen Sie wie es funktioniert.

Laden Sie einfach ein Bild auf die Nuxeo Platform:

Upload image in Nuxeo

Sie erhalten automatisch eine Miniaturansicht.

Thumbnail

Hier ist der Beweis dafür, dass meine Lambda-Funktionen die ganze Arbeit erledigt haben (natürlich sollten Sie es selbst ausprobieren, auch wenn sie mir vertrauen).

Thumbnail generated by Lambda

Thumbnail moved by Lambda