1. Genel Bakış
Bu codelab'de, Google App Engine'de kullanıcıların web uygulamasından resim yüklemesine ve yüklenen resimlere ve küçük resimlerine göz atmasına olanak tanıyan bir web ön ucu oluşturacaksınız.
Bu web uygulaması, iyi görünümlü bir kullanıcı arayüzüne sahip olmak için Bulma adlı bir CSS çerçevesini ve derleyeceğiniz uygulamanın API'sini çağırmak için Vue.JS JavaScript ön uç çerçevesini kullanacaktır.
Bu uygulama üç sekmeden oluşur:
- Yüklenen tüm görüntülerin küçük resimlerini ve resmi açıklayan etiketlerin listesini (Cloud Vision API tarafından önceki bir laboratuvarda algılananlar) gösterecek bir ana sayfa.
- Yüklenen en son 4 resimden oluşturulan kolajın gösterildiğ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/pictures
URL'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 sayfada bu verileri getirmek için Vue.js kullanılıyor. - Kolaj sayfası (
collage.html
), en son 4 resmi bir araya getirencollage.png
resmini gösterir. - Yükleme sayfası (
upload.html
), bir POST isteği aracılığıyla/api/pictures
URL'sine resim yüklemek için basit bir form sunar.
Neler öğreneceksiniz?
- App Engine
- Cloud Storage
- Cloud Firestore
2. Kurulum ve Gereksinimler
Kendi hızınızda ortam kurulumu
- Google Cloud Console'da oturum açıp 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 dizesidir ve bunu istediğiniz zaman güncelleyebilirsiniz.
- Proje Kimliği, tüm Google Cloud projelerinde benzersiz olmalıdır ve değiştirilemez (belirlendikten sonra değiştirilemez). Cloud Console, otomatik olarak benzersiz bir dize oluşturur. bunun ne olduğunu umursamıyorsunuz. Çoğu codelab'de, Proje Kimliğine referans vermeniz gerekir (ve bu kimlik genellikle
PROJECT_ID
olarak tanımlanır). Beğenmezseniz başka bir rastgele kod oluşturun ya da kendi proje kimliğinizi deneyip mevcut olup olmadığına bakın. Sıcaklık "soğudu" takip etmeniz gerekir. - Bazı API'lerin kullandığı üçüncü bir değer, yani Proje Numarası daha vardır. Bu değerlerin üçü 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 çalıştırmanın maliyeti, yüksek değildir. Bu eğitim dışında faturalandırmayla karşılaşmamak için kaynakları kapatmak istiyorsanız tüm "temizleme" işlemlerini uygulayın buradaki talimatları uygulayın. Yeni Google Cloud kullanıcıları, 300 ABD doları değerindeki ücretsiz denemeden yararlanabilir.
Cloud Shell'i başlatma
Google Cloud dizüstü bilgisayarınızdan uzaktan çalıştırılabilse de 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 bulunan Cloud Shell simgesini tıklayın:
Ortamı sağlamak ve bağlamak yalnızca birkaç dakika sürer. Tamamlandığında şuna benzer bir sonuç görmeniz gerekir:
İhtiyacınız olan tüm geliştirme araçlarını bu sanal makinede bulabilirsiniz. 5 GB boyutunda kalıcı bir ana dizin sunar ve Google Cloud üzerinde çalışarak ağ performansını ve kimlik doğrulamasını büyük ölçüde iyileştirir. Bu laboratuvardaki tüm çalışmalarınızı yalnızca bir tarayıcıyla yapabilirsiniz.
3. API'leri etkinleştir
App Engine için Compute Engine API gerekir. Etkin olduğundan emin olun:
gcloud services enable compute.googleapis.com
İşlemin başarıyla tamamlandığını göreceksiniz:
Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.
4. Kodu klonlama
Henüz yapmadıysanız kodu kontrol edin:
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ünde 3 dosyanız vardır:
index.js
, Node.js kodunu içerirpackage.json
, kitaplık bağımlılıklarını tanımlarapp.yaml
, Google App Engine iç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ırcollage.html
, son resimlerden oluşan kolajı gösterirupload.html
, yeni resimler yüklemek için bir form içeriyorapp.js
,index.html
sayfasını verilerle doldurmak için Vue.js'yi kullanıyorscript.js
, gezinme menüsünü ve "hamburger"i işler küçük ekranlarda simgestyle.css
bazı CSS yönergelerini tanımlar
5. Kodu inceleyin
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"
}
}
Başvurumuz ş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'nin web çerçevesi
- dayjs: tarihleri insan dostu bir şekilde gösteren küçük bir kitaplık
- bluebird: JavaScript taahhüt kitaplığı,
- express-fileupload: Dosya yüklemelerini kolayca işlemek için bir kitaplık.
Express ön uç
index.js
denetleyicisinin başında, daha önce package.json
içinde tanımlanan tüm bağımlılıklara ihtiyacınız olacak:
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ınpublic
alt dizininde bulunacağını belirtir.fileUpload()
, dosyaları/tmp
dizinindeki bellek içi dosya sistemine yerel olarak yüklemek için dosya yüklemeyi, dosya boyutunu 10 MB ile sınırlandıracak şekilde 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ı için HTML dosyaları bulunur. Bu sayfalar, API arka ucunu çağırır. Bu API aşağıdaki uç noktalara sahip olur:
POST /api/pictures
Upload.html dosyasındaki form aracılığıyla, resimler POST isteği aracılığıyla yüklenir.GET /api/pictures
Bu uç nokta, resimlerin listesini ve etiketlerini içeren bir JSON dokümanı döndürürGET /api/pictures/:name
Bu URL, tam boyutlu resmin bulut depolama konumuna yönlendiriyorGET /api/thumbnails/:name
Bu URL, küçük resmin bulut depolama konumuna yönlendiriyorGET /api/collage
Bu son URL, oluşturulan kolaj resminin bulut depolama konumuna yönlendirir
Resim yükleme
Node.js kodunu resim yükleme işlemini incelemeden önce public/upload.html
sayfasına hızlıca 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, bir HTTP POST yöntemi ve çok bölümlü bir biçimde /api/pictures
uç noktasını işaret ediyor. index.js
kullanıcısının artık bu uç noktaya ve yönteme yanıt vermesi ve dosyaları çıkarması gerekir:
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 de yüklenen dosyaların olup olmadığını kontrol edersiniz. Ardından, dosya yükleme düğümü modülümüzden gelen mv
yöntemini kullanarak dosyaları yerel olarak indirirsiniz. Dosyalar yerel dosya sisteminde kullanılabildiğine göre artık resimleri Cloud Storage paketine yükleyebilirsiniz. Son olarak, kullanıcıyı tekrar uygulamanın ana ekranına yönlendirirsiniz.
Resimleri listeleme
Güzel fotoğraflarınızı gösterme zamanı!
/api/pictures
işleyicide, küçük resmi oluşturulmuş tüm resimleri oluşturma tarihine göre sıralanmış şekilde almak için Firestore veritabanının pictures
koleksiyonuna bakarsınız.
Her resmi; adı, resmi açıklayan etiketler (Cloud Vision API'den gelir), baskın renk ve uygun oluşturulma tarihi (dayjs
ile "3 gün sonra" gibi göreli zaman farkı bilgileriyle birlikte) bir JavaScript dizisine aktarırsınız.
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 kumanda şu şekilde 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. Söz konusu sayfadaki işaretlemenin basitleştirilmiş bir sürümünü burada görebilirsiniz:
<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 öğesinin kimliği, Vue.js'nin dinamik olarak oluşturulacak işaretlemenin bir parçası olduğunu belirtir. Yinelemeler, v-for
yönergeleri sayesinde yapılır.
Cloud Vision API tarafından da görüldüğü gibi, resimlerdeki baskın renge karşılık gelen hoş renkli bir kenarlık olur. Ayrıca, bağlantı ve resim kaynaklarındaki küçük resimler ile tam genişlikteki resimleri gösteriyoruz.
Son olarak, resmi açıklayan etiketleri listeleriz.
Vue.js snippet'i (index.html
sayfasının alt kısmına içe aktarılan public/app.js
dosyasında) için JavaScript kodu:
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 bir AJAX çağrısı yapmak için Axios kitaplığını kullanır. Döndürülen veriler, daha önce gördüğünüz işaretlemedeki görünüm koduna bağlanır.
Resimleri görüntüleme
index.html
bağlantısından kullanıcılarımız resimlerin küçük resimlerini görüntüleyebilir, resimleri tam boyutlu olarak görüntülemek için bu resimleri tıklayabilir ve collage.html
üzerinden collage.png
resmi görüntüleyebilir.
Bu sayfaların HTML işaretlemesinde src
resmi ve href
bağlantısı bu 3 uç noktayı işaret ediyor ve resimler, küçük resimler ve kolajın Cloud Storage konumlarına yönlendirme yapıyor. Yolun HTML işaretlemesine sabit bir şekilde kodlanmasına gerek yoktur.
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ıktan sonra, Node.js uygulamanız başlatılmaya hazırdır. Express uygulaması varsayılan olarak 8080 numaralı bağlantı noktasında dinleme yapar ve gelen istekleri sunmaya 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 et
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ünün içinde, npm bağımlılarını yükleyin ve sunucuyu başlatın:
npm install; npm start
Her şey yolundaysa sunucu, 8080 numaralı bağlantı noktasından başlatılmalıdır:
Started web frontend service on port 8080 - Pictures bucket = uploaded-pictures-${GOOGLE_CLOUD_PROJECT} - Thumbnails bucket = thumbnails-${GOOGLE_CLOUD_PROJECT}
Paketlerinizin gerçek adları bu günlüklerde görünür ve hata ayıklama açısından faydalıdır.
Yerel olarak çalışan uygulamayı taramak için Cloud Shell'den web önizleme özelliğini kullanabilirsiniz:
Çıkmak için CTRL-C
tuşunu kullanın.
7. App Engine'e Dağıtım
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'u temel aldığını bildirir. Orijinal resimler ve küçük resimler için iki grubu işaret etmek üzere iki ortam değişkeni tanımlanmıştı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ğıtma
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ım:
gcloud app deploy
Bir veya iki dakika sonra, uygulamanın trafik sunduğu size 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, sürüm belirleme ve trafik bölme gibi App Engine ö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 (https://<YOUR_PROJECT_ID>.appspot.com/
) varsayılan App Engine URL'sine gidin. Ön uç kullanıcı arayüzünün çalışır durumda olduğunu göreceksiniz.
9. Temizleme (İsteğe bağlı)
Uygulamayı tutmak istemiyorsanız projenin tamamını silerek maliyet tasarrufu yapmak ve genel olarak iyi bir bulut vatandaşı olmak için kaynakları temizleyebilirsiniz:
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.
İşlediklerimiz
- App Engine
- Cloud Storage
- Cloud Firestore