1. Übersicht
In diesem Codelab erstellen Sie ein Web-Front-End in Google App Engine, mit dem Nutzer Bilder aus der Webanwendung hochladen und die hochgeladenen Bilder sowie deren Miniaturansichten durchsuchen können.
Für diese Webanwendung wird das CSS-Framework Bulma verwendet, um eine ansprechende Benutzeroberfläche zu bieten. Außerdem wird das JavaScript-Frontend-Framework Vue.JS verwendet, um die von Ihnen erstellte API der Anwendung aufzurufen.
Diese Anwendung besteht aus drei Registerkarten:
- Eine Startseite mit den Miniaturansichten aller hochgeladenen Bilder sowie der Liste der Labels, die das Bild beschreiben (die von der Cloud Vision API in einem vorherigen Lab erkannt wurden).
- Eine Collagenseite, auf der eine Collage aus den vier zuletzt hochgeladenen Bildern angezeigt wird.
- Eine Uploadseite, auf der Nutzer neue Bilder hochladen können
Das resultierende Front-End sieht so aus:
Diese drei Seiten sind einfache HTML-Seiten:
- Die Startseite (
index.html
) ruft den Node App Engine-Back-End-Code auf, um die Liste der Miniaturansichten und ihrer Labels über einen AJAX-Aufruf an die URL/api/pictures
abzurufen. Die Startseite verwendet zum Abrufen dieser Daten Vue.js. - Die Seite Collage (
collage.html
) zeigt auf das Bildcollage.png
, aus dem die vier neuesten Bilder zusammengesetzt sind. - Die Upload-Seite (
upload.html
) enthält ein einfaches Formular, mit dem ein Bild per POST-Anfrage an die URL/api/pictures
hochgeladen werden kann.
Lerninhalte
- App Engine
- Cloud Storage
- Cloud Firestore
2. Einrichtung und Anforderungen
Umgebung für das selbstbestimmte Lernen einrichten
- Melden Sie sich in der Google Cloud Console an und erstellen Sie ein neues Projekt oder verwenden Sie ein vorhandenes Projekt. Wenn Sie noch kein Gmail- oder Google Workspace-Konto haben, müssen Sie eines erstellen.
- Der Projektname ist der Anzeigename für die Projektteilnehmer. Es handelt sich um eine Zeichenfolge, die von Google APIs nicht verwendet wird und jederzeit aktualisiert werden kann.
- Die Projekt-ID muss für alle Google Cloud-Projekte eindeutig sein und ist unveränderlich. Sie kann nach dem Festlegen nicht mehr geändert werden. Die Cloud Console generiert automatisch einen eindeutigen String. ist Ihnen meist egal, was es ist. In den meisten Codelabs musst du auf die Projekt-ID verweisen, die in der Regel als
PROJECT_ID
identifiziert wird. Wenn es dir nicht gefällt, kannst du eine weitere zufällige Projekt-ID generieren. Du kannst aber auch selbst eine andere testen, um zu sehen, ob sie verfügbar ist. Dann ist es „eingefroren“ nachdem das Projekt erstellt wurde. - Es gibt einen dritten Wert, die Projektnummer, die von einigen APIs verwendet wird. Weitere Informationen zu allen drei Werten finden Sie in der Dokumentation.
- Als Nächstes müssen Sie in der Cloud Console die Abrechnung aktivieren, um Cloud-Ressourcen/APIs verwenden zu können. Dieses Codelab sollte ohne großen Aufwand betrieben werden. Wenn Sie Ressourcen beenden möchten, damit über diese Anleitung hinaus keine Kosten anfallen, führen Sie eine Bereinigung durch am Ende des Codelabs. Neue Google Cloud-Nutzer haben Anspruch auf eine kostenlose Testversion von 300$.
Cloud Shell starten
Sie können Google Cloud zwar von Ihrem Laptop aus aus der Ferne bedienen, in diesem Codelab verwenden Sie jedoch Google Cloud Shell, eine Befehlszeilenumgebung, die in der Cloud ausgeführt wird.
Klicken Sie in der Google Cloud Console rechts oben in der Symbolleiste auf das Cloud Shell-Symbol:
Die Bereitstellung und Verbindung mit der Umgebung dauert nur einen Moment. Wenn er abgeschlossen ist, sollten Sie in etwa Folgendes sehen:
Diese virtuelle Maschine verfügt über sämtliche Entwicklertools, die Sie benötigen. Es bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und läuft auf Google Cloud, wodurch die Netzwerkleistung und Authentifizierung erheblich verbessert werden. Sie können alle Aufgaben in diesem Lab ganz einfach in einem Browser erledigen.
3. APIs aktivieren
App Engine erfordert die Compute Engine API. Prüfen Sie, ob sie aktiviert ist:
gcloud services enable compute.googleapis.com
Der Vorgang sollte erfolgreich abgeschlossen sein:
Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.
4. Code klonen
Überprüfen Sie den Code, falls noch nicht geschehen:
git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop
Sie können dann zum Verzeichnis mit dem Front-End wechseln:
cd serverless-photosharing-workshop/frontend
Sie haben das folgende Dateilayout für das Front-End:
frontend | ├── index.js ├── package.json ├── app.yaml | ├── public | ├── index.html ├── collage.html ├── upload.html | ├── app.js ├── script.js ├── style.css
Im Stammverzeichnis unseres Projekts befinden sich drei Dateien:
index.js
enthält den Node.js-Code.package.json
definiert die Bibliotheksabhängigkeiten.app.yaml
ist die Konfigurationsdatei für Google App Engine.
Ein public
-Ordner enthält die statischen Ressourcen:
index.html
ist die Seite mit allen Miniaturansichten und Labels.collage.html
zeigt die Collage aus den letzten Bildernupload.html
enthält ein Formular zum Hochladen neuer Bilderapp.js
verwendet Vue.js, um die Seiteindex.html
mit Daten zu füllenscript.js
übernimmt das Navigationsmenü und den zugehörigen „Hamburger“ Symbol auf kleinen Bildschirmenstyle.css
definiert einige CSS-Anweisungen
5. Code ansehen
Abhängigkeiten
Die Datei package.json
definiert die erforderlichen Bibliotheksabhängigkeiten:
{
"name": "frontend",
"version": "0.0.1",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"@google-cloud/firestore": "^3.4.1",
"@google-cloud/storage": "^4.0.0",
"express": "^4.16.4",
"dayjs": "^1.8.22",
"bluebird": "^3.5.0",
"express-fileupload": "^1.1.6"
}
}
Unsere Bewerbung hängt von folgenden Faktoren ab:
- firestore: für den Zugriff auf Cloud Firestore mit unseren Bildmetadaten
- storage: Auf Google Cloud Storage zugreifen, in dem Bilder gespeichert sind
- express: das Web-Framework für Node.js
- dayjs: eine kleine Bibliothek, in der Datumsangaben übersichtlich dargestellt werden.
- bluebird: eine JavaScript-Promise-Bibliothek
- express-fileupload: eine Bibliothek zur einfachen Bearbeitung von Dateiuploads.
Express-Frontend
Am Anfang des index.js
-Controllers benötigen Sie alle Abhängigkeiten, die zuvor in package.json
definiert wurden:
const express = require('express');
const fileUpload = require('express-fileupload');
const Firestore = require('@google-cloud/firestore');
const Promise = require("bluebird");
const {Storage} = require('@google-cloud/storage');
const storage = new Storage();
const path = require('path');
const dayjs = require('dayjs');
const relativeTime = require('dayjs/plugin/relativeTime')
dayjs.extend(relativeTime)
Als Nächstes wird die Express-Anwendungsinstanz erstellt.
Es werden zwei Express-Middleware verwendet:
- Der
express.static()
-Aufruf gibt an, dass statische Ressourcen im Unterverzeichnispublic
verfügbar sind. - Und
fileUpload()
konfiguriert den Datei-Upload so, dass die Dateigröße auf 10 MB begrenzt wird, um die Dateien lokal in das speicherinterne Dateisystem im Verzeichnis/tmp
hochzuladen.
const app = express();
app.use(express.static('public'));
app.use(fileUpload({
limits: { fileSize: 10 * 1024 * 1024 },
useTempFiles : true,
tempFileDir : '/tmp/'
}))
Zu den statischen Ressourcen gehören die HTML-Dateien für die Startseite, die Collagenseite und die Upload-Seite. Diese Seiten rufen das API-Back-End auf. Diese API hat die folgenden Endpunkte:
POST /api/pictures
Bilder werden über das Formular in upload.html per POST-Anfrage hochgeladen.GET /api/pictures
Dieser Endpunkt gibt ein JSON-Dokument mit der Liste der Bilder und deren Labels zurückGET /api/pictures/:name
Diese URL leitet zum Cloud Storage-Speicherort des Bildes in voller Größe weiter.GET /api/thumbnails/:name
Diese URL leitet zum Cloud-Speicherort des Thumbnail-Bildes weiter.GET /api/collage
Diese letzte URL leitet zum Cloud-Speicherort des generierten Collagenbilds weiter.
Bilder hochladen
Bevor Sie sich mit dem Hochladen von Node.js-Code für Bilder befassen, werfen Sie einen Blick auf public/upload.html
.
...
<form method="POST" action="/api/pictures" enctype="multipart/form-data">
...
<input type="file" name="pictures">
<button>Submit</button>
...
</form>
...
Das Formularelement verweist mit einer HTTP-POST-Methode und einem mehrteiligen Format auf den Endpunkt /api/pictures
. index.js
muss jetzt auf diesen Endpunkt und diese Methode antworten und die Dateien extrahieren:
app.post('/api/pictures', async (req, res) => {
if (!req.files || Object.keys(req.files).length === 0) {
console.log("No file uploaded");
return res.status(400).send('No file was uploaded.');
}
console.log(`Receiving files ${JSON.stringify(req.files.pictures)}`);
const pics = Array.isArray(req.files.pictures) ? req.files.pictures : [req.files.pictures];
pics.forEach(async (pic) => {
console.log('Storing file', pic.name);
const newPicture = path.resolve('/tmp', pic.name);
await pic.mv(newPicture);
const pictureBucket = storage.bucket(process.env.BUCKET_PICTURES);
await pictureBucket.upload(newPicture, { resumable: false });
});
res.redirect('/');
});
Zuerst prüfen Sie, ob tatsächlich Dateien hochgeladen werden. Anschließend laden Sie die Dateien lokal über die Methode mv
aus unserem Dateiupload-Knotenmodul herunter. Nachdem die Dateien im lokalen Dateisystem verfügbar sind, laden Sie die Bilder in den Cloud Storage-Bucket hoch. Schließlich leiten Sie die Nutzenden zurück zum Hauptbildschirm der Anwendung.
Bilder auflisten
Zeit, Ihre schönen Bilder zu präsentieren!
Im /api/pictures
-Handler sehen Sie sich die pictures
-Sammlung der Firestore-Datenbank an, um alle Bilder abzurufen, deren Miniaturansicht generiert wurde. Sie sind nach absteigendem Erstellungsdatum sortiert.
Sie übertragen jedes Bild in einem JavaScript-Array, mit dem Namen, den Labels, die es beschreiben (aus der Cloud Vision API), der dominanten Farbe und einem nutzerfreundlichen Erstellungsdatum. Mit dayjs
wird die relative Zeitverschiebung wie „3 Tage ab heute“ angegeben.
app.get('/api/pictures', async (req, res) => {
console.log('Retrieving list of pictures');
const thumbnails = [];
const pictureStore = new Firestore().collection('pictures');
const snapshot = await pictureStore
.where('thumbnail', '==', true)
.orderBy('created', 'desc').get();
if (snapshot.empty) {
console.log('No pictures found');
} else {
snapshot.forEach(doc => {
const pic = doc.data();
thumbnails.push({
name: doc.id,
labels: pic.labels,
color: pic.color,
created: dayjs(pic.created.toDate()).fromNow()
});
});
}
console.table(thumbnails);
res.send(thumbnails);
});
Dieser Controller gibt Ergebnisse der folgenden Form zurück:
[
{
"name": "IMG_20180423_163745.jpg",
"labels": [
"Dish",
"Food",
"Cuisine",
"Ingredient",
"Orange chicken",
"Produce",
"Meat",
"Staple food"
],
"color": "#e78012",
"created": "a day ago"
},
...
]
Diese Datenstruktur wird von einem kleinen Vue.js-Snippet auf der index.html
-Seite verarbeitet. Hier ist eine vereinfachte Version des Markups von dieser Seite:
<div id="app">
<div class="container" id="app">
<div id="picture-grid">
<div class="card" v-for="pic in pictures">
<div class="card-content">
<div class="content">
<div class="image-border" :style="{ 'border-color': pic.color }">
<a :href="'/api/pictures/' + pic.name">
<img :src="'/api/thumbnails/' + pic.name">
</a>
</div>
<a class="panel-block" v-for="label in pic.labels" :href="'/?q=' + label">
<span class="panel-icon">
<i class="fas fa-bookmark"></i>
</span>
{{ label }}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
Die ID des div-Elements gibt an, dass Vue.js der Teil des Markups ist, der dynamisch gerendert wird. Die Iterationen erfolgen mithilfe der v-for
-Anweisungen.
Die Bilder erhalten einen farbigen Rahmen, der der dominanten Farbe auf dem Bild entspricht, wie sie von der Cloud Vision API gefunden wurde. Wir zeigen auf die Miniaturansichten und die Bilder mit voller Breite in den Links und Bildquellen.
Als Letztes listen wir die Labels auf, die das Bild beschreiben.
Hier ist der JavaScript-Code für das Vue.js-Snippet (in der public/app.js
-Datei, die unten auf der index.html
-Seite importiert wird):
var app = new Vue({
el: '#app',
data() {
return { pictures: [] }
},
mounted() {
axios
.get('/api/pictures')
.then(response => { this.pictures = response.data })
}
})
Der Vue-Code verwendet die Axios-Bibliothek, um einen AJAX-Aufruf an unseren /api/pictures
-Endpunkt zu senden. Die zurückgegebenen Daten werden dann an den Ansichtscode im Markup gebunden, das Sie zuvor gesehen haben.
Anzeigen der Bilder
Unter index.html
können unsere Nutzer die Miniaturansichten der Bilder ansehen, auf sie klicken, um sie in voller Größe anzuzeigen. Unter collage.html
sehen Nutzer das Bild collage.png
.
Im HTML-Markup dieser Seiten verweisen das Bild src
und der Link href
auf diese drei Endpunkte, die zu den Cloud Storage-Speicherorten der Bilder, Miniaturansichten und Collagen weiterleiten. Der Pfad muss im HTML-Markup nicht hartcodiert werden.
app.get('/api/pictures/:name', async (req, res) => {
res.redirect(`https://storage.cloud.google.com/${process.env.BUCKET_PICTURES}/${req.params.name}`);
});
app.get('/api/thumbnails/:name', async (req, res) => {
res.redirect(`https://storage.cloud.google.com/${process.env.BUCKET_THUMBNAILS}/${req.params.name}`);
});
app.get('/api/collage', async (req, res) => {
res.redirect(`https://storage.cloud.google.com/${process.env.BUCKET_THUMBNAILS}/collage.png`);
});
Knotenanwendung ausführen
Wenn alle Endpunkte definiert sind, kann Ihre Node.js-Anwendung gestartet werden. Die Express-Anwendung überwacht standardmäßig Port 8080 und kann eingehende Anfragen verarbeiten.
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Started web frontend service on port ${PORT}`);
console.log(`- Pictures bucket = ${process.env.BUCKET_PICTURES}`);
console.log(`- Thumbnails bucket = ${process.env.BUCKET_THUMBNAILS}`);
});
6. Lokal testen
Testen Sie den Code lokal, um sicherzustellen, dass er funktioniert, bevor Sie ihn in der Cloud bereitstellen.
Sie müssen die beiden Umgebungsvariablen exportieren, die den beiden Cloud Storage-Buckets entsprechen:
export BUCKET_THUMBNAILS=thumbnails-${GOOGLE_CLOUD_PROJECT} export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
Installieren Sie im Ordner frontend
die npm-Abhängigkeiten und starten Sie den Server:
npm install; npm start
Wenn alles gut gelaufen ist, sollte der Server auf Port 8080 gestartet werden:
Started web frontend service on port 8080 - Pictures bucket = uploaded-pictures-${GOOGLE_CLOUD_PROJECT} - Thumbnails bucket = thumbnails-${GOOGLE_CLOUD_PROJECT}
Die echten Namen Ihrer Buckets werden in diesen Logs angezeigt, was für die Fehlerbehebung hilfreich ist.
In Cloud Shell können Sie die Webvorschaufunktion verwenden, um die lokal ausgeführte Anwendung im Browser zu öffnen:
Verwende zum Beenden CTRL-C
.
7. Auf der App Engine bereitstellen
Ihre Anwendung kann bereitgestellt werden.
App Engine konfigurieren
Sehen Sie sich die Konfigurationsdatei app.yaml
für App Engine an:
runtime: nodejs16 env_variables: BUCKET_PICTURES: uploaded-pictures-GOOGLE_CLOUD_PROJECT BUCKET_THUMBNAILS: thumbnails-GOOGLE_CLOUD_PROJECT
In der ersten Zeile wird angegeben, dass die Laufzeit auf Node.js 10 basiert. Es sind zwei Umgebungsvariablen definiert, die auf die beiden Buckets verweisen: für die Originalbilder und für die Miniaturansichten.
Führen Sie den folgenden Befehl aus, um GOOGLE_CLOUD_PROJECT
durch Ihre tatsächliche Projekt-ID zu ersetzen:
sed -i -e "s/GOOGLE_CLOUD_PROJECT/${GOOGLE_CLOUD_PROJECT}/" app.yaml
Bereitstellen
Legen Sie Ihre bevorzugte Region für App Engine fest und verwenden Sie dieselbe Region wie in den vorherigen Labs:
gcloud config set compute/region europe-west1
Und stellen Sie Folgendes bereit:
gcloud app deploy
Nach ein bis zwei Minuten wird Ihnen mitgeteilt, dass die Anwendung Traffic verarbeitet:
Beginning deployment of service [default]... ╔════════════════════════════════════════════════════════════╗ ╠═ Uploading 8 files to Google Cloud Storage ═╣ ╚════════════════════════════════════════════════════════════╝ File upload done. Updating service [default]...done. Setting traffic split for service [default]...done. Deployed service [default] to [https://GOOGLE_CLOUD_PROJECT.appspot.com] You can stream logs from the command line by running: $ gcloud app logs tail -s default To view your application in the web browser run: $ gcloud app browse
Sie können auch den App Engine-Bereich der Cloud Console aufrufen, um zu sehen, ob die Anwendung bereitgestellt wurde, und sich Funktionen von App Engine wie Versionsverwaltung und Traffic-Aufteilung anzusehen:
8. App testen
Rufen Sie zum Testen die Standard-App Engine-URL für die Anwendung (https://<YOUR_PROJECT_ID>.appspot.com/
) auf. Sie sollten nun die Frontend-Benutzeroberfläche sehen und ausführen können.
9. Bereinigen (optional)
Wenn Sie die Anwendung nicht behalten möchten, können Sie Ressourcen bereinigen, um Kosten zu sparen und die Cloud insgesamt einwandfrei zu gestalten, indem Sie das gesamte Projekt löschen:
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
10. Glückwunsch!
Glückwunsch! Diese in App Engine gehostete Node.js-Webanwendung bindet alle Ihre Dienste zusammen und ermöglicht Ihren Nutzern, Bilder hochzuladen und zu visualisieren.
Behandelte Themen
- App Engine
- Cloud Storage
- Cloud Firestore