1. Genel Bakış
Bu codelab'de, Google App Engine'de bir web ön ucu oluşturacaksınız. Bu ön uç, kullanıcıların web uygulamasından resim yüklemesine, yüklenen resimlere ve küçük resimlerine göz atmasına olanak tanıyacak.

Bu web uygulaması, iyi görünümlü bir kullanıcı arayüzü için Bulma adlı bir CSS çerçevesi ve oluşturacağınız uygulamanın API'sini çağırmak için Vue.JS JavaScript ön uç çerçevesini kullanacak.
Bu uygulama üç sekmeden oluşur:
- Yüklenen tüm resimlerin küçük resimlerini ve resmi açıklayan etiketlerin listesini (önceki laboratuvarda Cloud Vision API tarafından algılananlar) gösterecek bir ana sayfa.
- Yüklenen en son 4 resimden oluşan kolajın gösterileceği bir kolaj sayfası.
- Kullanıcıların yeni resimler yükleyebileceği bir yükleme sayfası.
Elde edilen ön uç aşağıdaki gibi görünür:

Bu 3 sayfa basit HTML sayfalarıdır:
- Ana sayfa (
index.html),/api/picturesURL'sine yapılan bir AJAX çağrısıyla küçük resimlerin ve etiketlerinin listesini almak için Node App Engine arka uç kodunu çağırır. Ana sayfa, bu verileri getirmek için Vue.js'yi kullanıyor. - Kolaj sayfası (
collage.html), en son 4 resmi bir araya getirencollage.pngresmini gösterir. - Yükleme sayfası (
upload.html),/api/picturesURL'sine POST isteği aracılığıyla resim yüklemek için basit bir form sunar.
Neler öğreneceksiniz?
- App Engine
- Cloud Storage
- Cloud Firestore
2. Kurulum ve Gereksinimler
Yönlendirmesiz ortam kurulumu
- Google Cloud Console'da oturum açın ve yeni bir proje oluşturun veya mevcut bir projeyi yeniden kullanın. Gmail veya Google Workspace hesabınız yoksa hesap oluşturmanız gerekir.



- Proje adı, bu projenin katılımcıları için görünen addır. Google API'leri tarafından kullanılmayan bir karakter dizisidir ve istediğiniz zaman güncelleyebilirsiniz.
- Proje kimliği, tüm Google Cloud projelerinde benzersiz olmalı ve sabittir (ayarlandıktan sonra değiştirilemez). Cloud Console, benzersiz bir dizeyi otomatik olarak oluşturur. Genellikle bu dizenin ne olduğuyla ilgilenmezsiniz. Çoğu codelab'de proje kimliğine (genellikle
PROJECT_IDolarak tanımlanır) başvurmanız gerekir. Bu nedenle, beğenmezseniz başka bir rastgele kimlik oluşturabilir veya kendi kimliğinizi deneyip kullanılabilir olup olmadığını görebilirsiniz. Proje oluşturulduktan sonra bu değer "dondurulur". - Bazı API'lerin kullandığı üçüncü bir değer olan Proje Numarası da vardır. Bu üç değer hakkında daha fazla bilgiyi belgelerde bulabilirsiniz.
- Ardından, Cloud kaynaklarını/API'lerini kullanmak için Cloud Console'da faturalandırmayı etkinleştirmeniz gerekir. Bu codelab'i tamamlamak neredeyse hiç maliyetli değildir. Bu eğitimin ötesinde faturalandırma ücreti alınmaması için kaynakları kapatmak üzere codelab'in sonunda bulunan "temizleme" talimatlarını uygulayın. Google Cloud'un yeni kullanıcıları 300 ABD doları değerinde ücretsiz deneme programından yararlanabilir.
Cloud Shell'i başlatma
Google Cloud, dizüstü bilgisayarınızdan uzaktan çalıştırılabilir. Ancak bu codelab'de, Cloud'da çalışan bir komut satırı ortamı olan Google Cloud Shell'i kullanacaksınız.
Google Cloud Console'da sağ üstteki araç çubuğunda Cloud Shell simgesini tıklayın:

Ortamın temel hazırlığı ve bağlanması yalnızca birkaç dakikanızı alır. İşlem tamamlandığında aşağıdakine benzer bir sonuç görürsünüz:

Bu sanal makine, ihtiyaç duyacağınız tüm geliştirme araçlarını içerir. 5 GB boyutunda kalıcı bir ana dizin sunar ve Google Cloud üzerinde çalışır. Bu sayede ağ performansı ve kimlik doğrulama önemli ölçüde güçlenir. Bu laboratuvardaki çalışmalarınızın tamamını yalnızca bir tarayıcı kullanarak yapabilirsiniz.
3. API'leri etkinleştir
App Engine için Compute Engine API gerekir. Etkinleştirildiğinden emin olun:
gcloud services enable compute.googleapis.com
İşlemin başarıyla tamamlandığını görmeniz gerekir:
Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.
4. Kodu klonlayın
Henüz yapmadıysanız kodu kullanın:
git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop
Ardından, ön ucu içeren dizine gidebilirsiniz:
cd serverless-photosharing-workshop/frontend
Ön uç için aşağıdaki dosya düzenine sahip olursunuz:
frontend
|
├── index.js
├── package.json
├── app.yaml
|
├── public
|
├── index.html
├── collage.html
├── upload.html
|
├── app.js
├── script.js
├── style.css
Projemizin kök dizininde 3 dosya var:
index.js, Node.js kodunu içerir.package.json, kitaplık bağımlılıklarını tanımlar.app.yaml, Google App Engine'in yapılandırma dosyasıdır.
public klasörü statik kaynakları içerir:
index.html, tüm küçük resimleri ve etiketleri gösteren sayfadır.collage.htmlsimgesi, son resimlerin kolajını gösterir.upload.htmlyeni resimler yüklemek için bir form içeriyorapp.js,index.htmlsayfasını verilerle doldurmak için Vue.js'yi kullanıyorscript.js, küçük ekranlarda gezinme menüsünü ve "hamburger" simgesini yönetir.style.cssbazı CSS yönergelerini tanımlar
5. Kodu keşfetme
Bağımlılıklar
package.json dosyası, gerekli kitaplık bağımlılıklarını tanımlar:
{
"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"
}
}
Uygulamamız şunlara bağlıdır:
- firestore: Resim meta verilerimizle Cloud Firestore'a erişmek için
- storage: Resimlerin depolandığı Google Cloud Storage'a erişmek için
- express: Node.js için web çerçevesi,
- dayjs: Tarihleri kullanıcı dostu bir şekilde göstermek için kullanılan küçük bir kitaplık,
- bluebird: JavaScript sözü kitaplığı,
- express-fileupload: Dosya yüklemelerini kolayca işlemek için kullanılan bir kitaplık.
Express ön uç
index.js denetleyicisinin başında, daha önce package.json içinde tanımlanan tüm bağımlılıkları gerektirirsiniz:
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)
Ardından, Express uygulama örneği oluşturulur.
İki Express ara katman yazılımı kullanılır:
express.static()çağrısı, statik kaynaklarınpublicalt dizininde kullanılabileceğini gösterir.fileUpload(), dosya boyutunu 10 MB ile sınırlamak ve dosyaları/tmpdizinindeki bellek içi dosya sistemine yerel olarak yüklemek için dosya yüklemeyi yapılandırır.
const app = express();
app.use(express.static('public'));
app.use(fileUpload({
limits: { fileSize: 10 * 1024 * 1024 },
useTempFiles : true,
tempFileDir : '/tmp/'
}))
Statik kaynaklar arasında ana sayfa, kolaj sayfası ve yükleme sayfasının HTML dosyaları yer alır. Bu sayfalar, API arka ucunu çağırır. Bu API'nin aşağıdaki uç noktaları olacaktır:
POST /api/picturesupload.html dosyasındaki form aracılığıyla resimler, POST isteğiyle yüklenir.GET /api/picturesBu uç nokta, resimlerin listesini ve etiketlerini içeren bir JSON belgesi döndürür.GET /api/pictures/:nameBu URL, tam boyutlu resmin bulut depolama alanındaki konumuna yönlendirir.GET /api/thumbnails/:nameBu URL, küçük resmin bulut depolama alanı konumuna yönlendirir.GET /api/collageBu son URL, oluşturulan kolaj resminin bulut depolama konumuna yönlendirir.
Resim yükleme
Resim yükleme Node.js kodunu incelemeden önce public/upload.html bölümüne göz atın.
...
<form method="POST" action="/api/pictures" enctype="multipart/form-data">
...
<input type="file" name="pictures">
<button>Submit</button>
...
</form>
...
Form öğesi, HTTP POST yöntemi ve çok parçalı biçimle /api/pictures uç noktasına işaret eder. index.js artık bu uç noktaya ve yönteme yanıt verip dosyaları ayıklamalıdır:
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('/');
});
Öncelikle, gerçekten dosya yüklenip yüklenmediğini kontrol edin. Ardından, dosya yükleme Node modülümüzden gelen mv yöntemiyle dosyaları yerel olarak indirirsiniz. Dosyalar yerel dosya sisteminde kullanılabilir hale geldiğinde resimleri Cloud Storage paketine yüklersiniz. Son olarak, kullanıcıyı uygulamanın ana ekranına geri yönlendirirsiniz.
Resimleri listeleme
Güzel resimlerinizi gösterme zamanı!
/api/pictures işleyicisinde, oluşturulma tarihi azalan şekilde sıralanmış tüm resimleri (küçük resmi oluşturulmuş) almak için Firestore veritabanının pictures koleksiyonuna bakarsınız.
Her resmi, adıyla, resmi açıklayan etiketlerle (Cloud Vision API'den alınır), baskın renkle ve kolay anlaşılır bir oluşturma tarihiyle (dayjs ile "3 gün sonra" gibi göreceli zaman farkları kullanırız) bir JavaScript dizisine gönderirsiniz.
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);
});
Bu denetleyici, aşağıdaki şekle sahip sonuçlar döndürür:
[
{
"name": "IMG_20180423_163745.jpg",
"labels": [
"Dish",
"Food",
"Cuisine",
"Ingredient",
"Orange chicken",
"Produce",
"Meat",
"Staple food"
],
"color": "#e78012",
"created": "a day ago"
},
...
]
Bu veri yapısı, index.html sayfasındaki küçük bir Vue.js snippet'i tarafından kullanılır. İşaretlemenin basitleştirilmiş bir sürümünü aşağıda bulabilirsiniz:
<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>
Div'in kimliği, Vue.js'e bunun dinamik olarak oluşturulacak işaretlemenin bir parçası olduğunu gösterir. Yinelemeler, v-for yönergeleri sayesinde yapılır.
Resimler, Cloud Vision API tarafından belirlenen ve resimdeki baskın renge karşılık gelen güzel bir renkli kenarlıkla gösterilir. Ayrıca, bağlantı ve resim kaynaklarındaki küçük resimlere ve tam genişlikteki resimlere işaret ederiz.
Son olarak, resmi açıklayan etiketleri listeliyoruz.
Vue.js snippet'inin JavaScript kodu (public/app.js sayfasının en altına içe aktarılan index.html dosyasında):
var app = new Vue({
el: '#app',
data() {
return { pictures: [] }
},
mounted() {
axios
.get('/api/pictures')
.then(response => { this.pictures = response.data })
}
})
Vue kodu, /api/pictures uç noktamıza AJAX çağrısı yapmak için Axios kitaplığını kullanıyor. Döndürülen veriler daha sonra, daha önce gördüğünüz işaretlemedeki görünüm koduna bağlanır.
Resimleri görüntüleme
index.html simgesinden kullanıcılar resimlerin küçük resimlerini görüntüleyebilir, tam boyutlu resimleri görüntülemek için küçük resimleri tıklayabilir ve collage.html simgesinden collage.png resmini görüntüleyebilir.
Bu sayfaların HTML işaretlemesinde, resim src ve bağlantı href, resimlerin, küçük resimlerin ve kolajın Cloud Storage konumlarına yönlendiren bu 3 uç noktayı gösterir. Yolu HTML işaretlemesinde sabit kodlamanız gerekmez.
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`);
});
Node uygulamasını çalıştırma
Tüm uç noktalar tanımlandığında Node.js uygulamanız başlatılmaya hazır olur. Express uygulaması varsayılan olarak 8080 numaralı bağlantı noktasını dinler ve gelen istekleri karşılamaya hazırdır.
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. Yerel olarak test etme
Buluta dağıtmadan önce çalıştığından emin olmak için kodu yerel olarak test edin.
İki Cloud Storage paketine karşılık gelen iki ortam değişkenini dışa aktarmanız gerekir:
export BUCKET_THUMBNAILS=thumbnails-${GOOGLE_CLOUD_PROJECT}
export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
frontend klasöründe npm bağımlılıklarını yükleyin ve sunucuyu başlatın:
npm install; npm start
Her şey yolunda gittiyse sunucu 8080 numaralı bağlantı noktasında başlatılır:
Started web frontend service on port 8080
- Pictures bucket = uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
- Thumbnails bucket = thumbnails-${GOOGLE_CLOUD_PROJECT}
Bu günlüklerde, hata ayıklama amacıyla kullanışlı olan gerçek paket adlarınız gösterilir.
Cloud Shell'den, yerel olarak çalışan uygulamaya göz atmak için web önizleme özelliğini kullanabilirsiniz:

Çıkmak için CTRL-C öğesini kullanın.
7. App Engine'e dağıtma
Uygulamanız dağıtılmaya hazır.
App Engine'i yapılandırma
App Engine için app.yaml yapılandırma dosyasını inceleyin:
runtime: nodejs16 env_variables: BUCKET_PICTURES: uploaded-pictures-GOOGLE_CLOUD_PROJECT BUCKET_THUMBNAILS: thumbnails-GOOGLE_CLOUD_PROJECT
İlk satır, çalışma zamanının Node.js 10'a dayandığını belirtir. Orijinal resimler ve küçük resimler için iki pakete işaret eden iki ortam değişkeni tanımlanır.
GOOGLE_CLOUD_PROJECT öğesini gerçek proje kimliğinizle değiştirmek için aşağıdaki komutu çalıştırabilirsiniz:
sed -i -e "s/GOOGLE_CLOUD_PROJECT/${GOOGLE_CLOUD_PROJECT}/" app.yaml
Dağıt
App Engine için tercih ettiğiniz bölgeyi ayarlayın. Önceki laboratuvarlarda aynı bölgeyi kullandığınızdan emin olun:
gcloud config set compute/region europe-west1
Ve dağıtın:
gcloud app deploy
Bir veya iki dakika sonra, uygulamanın trafik sunduğu bildirilir:
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
Uygulamanın dağıtıldığını görmek ve App Engine'in sürüm oluşturma ve trafiği bölme gibi özelliklerini keşfetmek için Cloud Console'un App Engine bölümünü de ziyaret edebilirsiniz:

8. Uygulamayı test etme
Test etmek için uygulamanın varsayılan App Engine URL'sine (https://<YOUR_PROJECT_ID>.appspot.com/) gidin. Ön uç kullanıcı arayüzünün çalışır durumda olduğunu görmelisiniz.

9. Temizleme (isteğe bağlı)
Uygulamayı tutmayı düşünmüyorsanız maliyetleri düşürmek ve genel olarak iyi bir bulut kullanıcısı olmak için kaynakları temizleyebilir, bunun için de projenin tamamını silebilirsiniz:
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
10. Tebrikler!
Tebrikler! App Engine'de barındırılan bu Node.js web uygulaması, tüm hizmetlerinizi birbirine bağlar ve kullanıcılarınızın resim yükleyip görselleştirmesine olanak tanır.
İşlediğimiz konular
- App Engine
- Cloud Storage
- Cloud Firestore