Pic-a-day: Lab 4 — یک صفحه وب ایجاد کنید

1. بررسی اجمالی

در این لبه کد، شما یک صفحه وب در Google App Engine ایجاد می‌کنید که به کاربران اجازه می‌دهد تصاویر را از برنامه وب آپلود کنند و همچنین تصاویر آپلود شده و تصاویر کوچک آنها را مرور کنند.

21741cd63b425aeb.png

این برنامه وب از یک فریم ورک CSS به نام Bulma برای داشتن رابط کاربری خوب و همچنین از فریم ورک Vue.JS JavaScript frontend برای فراخوانی API برنامه ای که می سازید استفاده می کند.

این برنامه از سه تب تشکیل شده است:

  • یک صفحه اصلی که تصاویر کوچک همه تصاویر آپلود شده را به همراه لیست برچسب هایی که تصویر را توصیف می کنند (آنهایی که توسط Cloud Vision API در آزمایشگاه قبلی شناسایی شده اند) نمایش می دهد.
  • یک صفحه کلاژ که کلاژ ساخته شده از 4 عکس اخیر آپلود شده را نشان می دهد.
  • یک صفحه آپلود که در آن کاربران می توانند تصاویر جدید را آپلود کنند.

ظاهر نهایی به صورت زیر است:

6a4d5e5603ba4b73.png

آن 3 صفحه صفحات ساده HTML هستند:

  • صفحه اصلی ( index.html ) کد پشتیبان Node App Engine را فراخوانی می کند تا لیستی از تصاویر کوچک و برچسب های آنها را از طریق یک تماس AJAX به آدرس /api/pictures دریافت کند. صفحه اصلی از Vue.js برای واکشی این داده ها استفاده می کند.
  • صفحه کلاژ ( collage.html ) به تصویر collage.png اشاره می کند که 4 عکس جدید را جمع آوری می کند.
  • صفحه آپلود ( upload.html ) یک فرم ساده برای آپلود یک عکس از طریق یک درخواست POST در /api/pictures URL ارائه می دهد.

چیزی که یاد خواهید گرفت

  • موتور برنامه
  • فضای ذخیره سازی ابری
  • Cloud Firestore

2. راه اندازی و الزامات

تنظیم محیط خود به خود

  1. به Google Cloud Console وارد شوید و یک پروژه جدید ایجاد کنید یا از یک موجود استفاده مجدد کنید. اگر قبلاً یک حساب Gmail یا Google Workspace ندارید، باید یک حساب ایجاد کنید .

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • نام پروژه نام نمایشی برای شرکت کنندگان این پروژه است. این یک رشته کاراکتری است که توسط API های Google استفاده نمی شود و می توانید هر زمان که بخواهید آن را به روز کنید.
  • شناسه پروژه باید در تمام پروژه‌های Google Cloud منحصربه‌فرد باشد و تغییرناپذیر باشد (پس از تنظیم نمی‌توان آن را تغییر داد). Cloud Console به طور خودکار یک رشته منحصر به فرد تولید می کند. معمولاً برای شما مهم نیست که چیست. در اکثر کدها، باید به شناسه پروژه ارجاع دهید (و معمولاً به عنوان PROJECT_ID شناخته می‌شود)، بنابراین اگر آن را دوست ندارید، یک نمونه تصادفی دیگر ایجاد کنید، یا می‌توانید شناسه پروژه را امتحان کنید و ببینید در دسترس است. سپس پس از ایجاد پروژه "یخ زده" می شود.
  • یک مقدار سوم وجود دارد، یک شماره پروژه که برخی از API ها از آن استفاده می کنند. در مورد هر سه این مقادیر در مستندات بیشتر بیاموزید.
  1. در مرحله بعد، برای استفاده از منابع Cloud/APIها، باید صورتحساب را در کنسول Cloud فعال کنید . اجرا کردن از طریق این کد لبه نباید هزینه زیادی داشته باشد، اگر اصلاً باشد. برای اینکه منابع را خاموش کنید تا بیش از این آموزش متحمل صورتحساب نشوید، دستورالعمل‌های «پاک‌سازی» را که در انتهای Codelab یافت می‌شود دنبال کنید. کاربران جدید Google Cloud واجد شرایط برنامه آزمایشی رایگان 300 دلاری هستند.

Cloud Shell را راه اندازی کنید

در حالی که Google Cloud را می توان از راه دور از لپ تاپ شما کار کرد، در این کد لبه از Google Cloud Shell استفاده خواهید کرد، یک محیط خط فرمان که در Cloud اجرا می شود.

از Google Cloud Console ، روی نماد Cloud Shell در نوار ابزار بالا سمت راست کلیک کنید:

55efc1aaa7a4d3ad.png

تهیه و اتصال به محیط فقط چند لحظه طول می کشد. وقتی تمام شد، باید چیزی شبیه به این را ببینید:

7ffe5cbb04455448.png

این ماشین مجازی با تمام ابزارهای توسعه که شما نیاز دارید بارگذاری شده است. این یک فهرست اصلی 5 گیگابایتی دائمی را ارائه می دهد و در Google Cloud اجرا می شود و عملکرد و احراز هویت شبکه را تا حد زیادی افزایش می دهد. تمام کارهای شما در این آزمایشگاه به سادگی با یک مرورگر قابل انجام است.

3. API ها را فعال کنید

App Engine به Compute Engine API نیاز دارد. مطمئن شوید که فعال است:

gcloud services enable compute.googleapis.com

شما باید عملیات را با موفقیت کامل ببینید:

Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.

4. کد را شبیه سازی کنید

اگر قبلاً این کار را نکرده اید، کد را بررسی کنید:

git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop

سپس می توانید به دایرکتوری حاوی frontend بروید:

cd serverless-photosharing-workshop/frontend

شما طرح بندی فایل زیر را برای frontend خواهید داشت:

frontend
 |
 ├── index.js
 ├── package.json
 ├── app.yaml
 |
 ├── public
      |
      ├── index.html
      ├── collage.html
      ├── upload.html
      |
      ├── app.js
      ├── script.js
      ├── style.css

در ریشه پروژه ما، شما 3 فایل دارید:

  • index.js حاوی کد Node.js است
  • package.json وابستگی های کتابخانه را تعریف می کند
  • app.yaml فایل پیکربندی Google App Engine است

یک پوشه public حاوی منابع استاتیک است:

  • index.html صفحه ای است که تمام تصاویر کوچک و برچسب ها را نشان می دهد
  • collage.html کلاژ تصاویر اخیر را نشان می دهد
  • upload.html حاوی فرمی برای آپلود تصاویر جدید است
  • app.js از Vue.js برای پر کردن صفحه index.html با داده ها استفاده می کند
  • script.js منوی پیمایش و نماد "همبرگر" آن را روی صفحه های کوچک کنترل می کند
  • style.css برخی از دستورات CSS را تعریف می کند

5. کد را کاوش کنید

وابستگی ها

فایل package.json وابستگی های کتابخانه مورد نیاز را تعریف می کند:

{
  "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"
  }
}

درخواست ما بستگی به موارد زیر دارد:

  • firestore : برای دسترسی به Cloud Firestore با ابرداده تصویر ما،
  • Storage : برای دسترسی به Google Cloud Storage که در آن تصاویر ذخیره می شوند،
  • express : چارچوب وب برای Node.js،
  • dayjs : یک کتابخانه کوچک برای نشان دادن تاریخ ها به روشی انسان پسند،
  • bluebird : یک کتابخانه وعده جاوا اسکریپت،
  • express-fileupload : کتابخانه ای برای مدیریت آسان آپلود فایل.

جلوی اکسپرس

در ابتدای کنترلر index.js ، به تمام وابستگی هایی که قبلاً در package.json تعریف شده بود نیاز دارید:

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)

بعد، نمونه برنامه Express ایجاد می شود.

دو میان افزار Express استفاده می شود:

  • فراخوانی express.static() نشان می دهد که منابع استاتیک در زیر شاخه public در دسترس خواهند بود.
  • و fileUpload() آپلود فایل را پیکربندی می‌کند تا اندازه فایل را به 10 مگابایت محدود کند، تا فایل‌ها را به صورت محلی در سیستم فایل درون حافظه در فهرست /tmp آپلود کند.
const app = express();
app.use(express.static('public'));
app.use(fileUpload({
    limits: { fileSize: 10 * 1024 * 1024 },
    useTempFiles : true,
    tempFileDir : '/tmp/'
}))

در میان منابع استاتیک، فایل های HTML برای صفحه اصلی، صفحه کلاژ و صفحه آپلود را دارید. این صفحات باطن API تماس خواهند گرفت. این API دارای نقاط پایانی زیر خواهد بود:

  • POST /api/pictures از طریق فرم در upload.html، تصاویر از طریق درخواست POST آپلود خواهند شد.
  • GET /api/pictures این نقطه پایانی یک سند JSON حاوی لیست تصاویر و برچسب های آنها را برمی گرداند.
  • GET /api/pictures/:name این URL به مکان ذخیره سازی ابری تصویر در اندازه کامل هدایت می شود.
  • GET /api/thumbnails/:name این URL به محل ذخیره سازی ابری تصویر کوچک هدایت می شود.
  • GET /api/collage این آخرین URL به محل ذخیره سازی ابری تصویر کلاژ تولید شده هدایت می شود.

آپلود تصویر

قبل از کاوش در کد آپلود تصویر Node.js، نگاهی گذرا به public/upload.html بیندازید.

... 
<form method="POST" action="/api/pictures" enctype="multipart/form-data">
    ... 
    <input type="file" name="pictures">
    <button>Submit</button>
    ... 
</form>
... 

عنصر فرم به نقطه پایانی /api/pictures با یک روش HTTP POST و یک قالب چند قسمتی اشاره می کند. index.js اکنون باید به آن نقطه و روش پاسخ دهد و فایل ها را استخراج کند:

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('/');
});

ابتدا بررسی می کنید که آیا واقعاً فایل هایی در حال آپلود هستند. سپس فایل ها را به صورت محلی از طریق روش mv که از ماژول آپلود فایل Node ما می آید دانلود می کنید. اکنون که فایل ها در سیستم فایل محلی در دسترس هستند، تصاویر را در سطل ذخیره سازی ابری آپلود می کنید. در نهایت، کاربر را به صفحه اصلی برنامه هدایت می‌کنید.

لیست کردن تصاویر

زمان نمایش تصاویر زیبای شماست!

در کنترل کننده /api/pictures ، شما به مجموعه pictures پایگاه داده Firestore نگاه می کنید تا تمام تصاویر (که تصویر کوچک آنها تولید شده است) را که بر اساس تاریخ نزولی ایجاد شده اند، بازیابی کنید.

شما هر تصویر را در یک آرایه جاوا اسکریپت، با نام آن، برچسب‌هایی که آن را توصیف می‌کنند (از Cloud Vision API)، رنگ غالب و تاریخ ایجاد دوستانه (با dayjs ، زمان نسبی جبران می‌کنیم مانند "3 روز بعد" فشار می‌دهید. " ).

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);
});

این کنترلر نتایج شکل زیر را برمی گرداند:

[
   {
      "name": "IMG_20180423_163745.jpg",
      "labels": [
         "Dish",
         "Food",
         "Cuisine",
         "Ingredient",
         "Orange chicken",
         "Produce",
         "Meat",
         "Staple food"
      ],
      "color": "#e78012",
      "created": "a day ago"
   },
   ...
]

این ساختار داده توسط یک قطعه کوچک Vue.js از صفحه index.html مصرف می شود. در اینجا یک نسخه ساده از نشانه گذاری از آن صفحه است:

<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> &nbsp;
                                                        </span>
                                                        {{ label }}
                                                </a>
                                        </div>
                                </div>
                        </div>
            </div>
        </div>
</div>

شناسه div به Vue.js نشان می دهد که بخشی از نشانه گذاری است که به صورت پویا ارائه می شود. تکرارها به لطف دستورالعمل های v-for انجام می شوند.

همانطور که توسط Cloud Vision API یافت می شود و ما به تصاویر بندانگشتی و تصاویر تمام عرض در پیوند و منابع تصویر اشاره می کنیم، حاشیه های رنگی خوبی مطابق با رنگ غالب در تصویر دریافت می کنند.

در آخر، ما برچسب هایی را که تصویر را توصیف می کنند فهرست می کنیم.

در اینجا کد جاوا اسکریپت برای قطعه Vue.js (در فایل public/app.js وارد شده در پایین صفحه index.html ) آمده است:

var app = new Vue({
  el: '#app',
  data() {
    return { pictures: [] }
  },
  mounted() {
    axios
      .get('/api/pictures')
      .then(response => { this.pictures = response.data })
  }
})

کد Vue از کتابخانه Axios برای برقراری تماس AJAX با نقطه پایانی /api/pictures ما استفاده می کند. سپس داده های برگشتی به کد view در نشانه گذاری که قبلاً مشاهده کردید، متصل می شوند.

مشاهده تصاویر

از index.html کاربران ما می توانند ریز عکسها را مشاهده کنند، برای مشاهده تصاویر در اندازه واقعی روی آنها کلیک کنند و fom collage.html ، کاربران تصویر collage.png را مشاهده کنند.

در نشانه‌گذاری HTML آن صفحات، تصویر src و href به آن 3 نقطه پایانی اشاره می‌کند که به مکان‌های ذخیره‌سازی ابری تصاویر، تصاویر کوچک و کلاژ هدایت می‌شوند. نیازی به کدگذاری سخت مسیر در نشانه گذاری HTML نیست.

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

با تمام نقاط پایانی تعریف شده، برنامه Node.js شما آماده راه اندازی است. برنامه Express به طور پیش فرض به پورت 8080 گوش می دهد و آماده ارائه درخواست های دریافتی است.

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. تست محلی

کد را به صورت محلی تست کنید تا مطمئن شوید قبل از استقرار در فضای ابری کار می کند.

شما باید دو متغیر محیطی مربوط به دو سطل Cloud Storage را صادر کنید:

export BUCKET_THUMBNAILS=thumbnails-${GOOGLE_CLOUD_PROJECT}
export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}

در داخل پوشه frontend ، وابستگی های npm را نصب کنید و سرور را راه اندازی کنید:

npm install; npm start

اگر همه چیز خوب پیش رفت، باید سرور را روی پورت 8080 راه اندازی کند:

Started web frontend service on port 8080
- Pictures bucket = uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
- Thumbnails bucket = thumbnails-${GOOGLE_CLOUD_PROJECT}

نام واقعی سطل های شما در آن گزارش ها ظاهر می شود، که برای اهداف اشکال زدایی مفید است.

از Cloud Shell، می‌توانید از ویژگی پیش‌نمایش وب برای مرورگر برنامه‌ای که به صورت محلی اجرا می‌شود، استفاده کنید:

82fa3266d48c0d0a.png

برای خروج از CTRL-C استفاده کنید.

7. استقرار به App Engine

برنامه شما آماده استقرار است.

موتور برنامه را پیکربندی کنید

فایل پیکربندی app.yaml را برای App Engine بررسی کنید:

runtime: nodejs16
env_variables:
  BUCKET_PICTURES: uploaded-pictures-GOOGLE_CLOUD_PROJECT
  BUCKET_THUMBNAILS: thumbnails-GOOGLE_CLOUD_PROJECT

خط اول اعلام می‌کند که زمان اجرا بر اساس Node.js 10 است. دو متغیر محیطی برای اشاره به دو سطل، برای تصاویر اصلی و برای ریز عکس‌ها، تعریف شده‌اند.

برای جایگزینی GOOGLE_CLOUD_PROJECT با شناسه پروژه واقعی خود، می توانید دستور زیر را اجرا کنید:

sed -i -e "s/GOOGLE_CLOUD_PROJECT/${GOOGLE_CLOUD_PROJECT}/" app.yaml

مستقر کنید

منطقه دلخواه خود را برای App Engine تنظیم کنید، حتماً از همان منطقه در آزمایشگاه های قبلی استفاده کنید:

gcloud config set compute/region europe-west1

و استقرار:

gcloud app deploy

پس از یک یا دو دقیقه، به شما گفته می شود که برنامه در حال ارائه ترافیک است:

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

همچنین می‌توانید از بخش App Engine در Cloud Console دیدن کنید تا ببینید که برنامه مستقر است و ویژگی‌های App Engine مانند نسخه‌سازی و تقسیم ترافیک را بررسی کنید:

db0e196b00fceab1.png

8. برنامه را تست کنید

برای آزمایش، به نشانی وب پیش‌فرض App Engine برای برنامه ( https://<YOUR_PROJECT_ID>.appspot.com/ ) بروید و باید رابط کاربری frontend را ببینید که در حال اجراست!

6a4d5e5603ba4b73.png

9. تمیز کردن (اختیاری)

اگر قصد ندارید برنامه را نگه دارید، می توانید با حذف کل پروژه، منابع را پاکسازی کنید تا در هزینه ها صرفه جویی کنید و در کل شهروند ابری خوبی باشید:

gcloud projects delete ${GOOGLE_CLOUD_PROJECT} 

10. تبریک می گویم!

تبریک می گویم! این برنامه وب Node.js که در App Engine میزبانی شده است، همه سرویس های شما را به هم متصل می کند و به کاربران شما اجازه می دهد تصاویر را آپلود و تجسم کنند.

آنچه را پوشش داده ایم

  • موتور برنامه
  • فضای ذخیره سازی ابری
  • Cloud Firestore

مراحل بعدی