1. Обзор
В этой лаборатории кода вы создадите веб-интерфейс на Google App Engine, который позволит пользователям загружать изображения из веб-приложения, а также просматривать загруженные изображения и их миниатюры.
Это веб-приложение будет использовать CSS-фреймворк под названием Bulma для создания красивого пользовательского интерфейса, а также интерфейсную среду JavaScript Vue.JS для вызова API приложения, которое вы создадите.
Это приложение будет состоять из трёх вкладок:
- Домашняя страница, на которой будут отображаться миниатюры всех загруженных изображений, а также список меток, описывающих изображение (те, которые были обнаружены Cloud Vision API в предыдущем лабораторном исследовании).
- Страница коллажей , на которой будет показан коллаж, сделанный из 4 последних загруженных изображений.
- Страница загрузки , на которую пользователи могут загружать новые изображения.
В результате интерфейс выглядит следующим образом:
Эти 3 страницы представляют собой простые HTML-страницы:
- Домашняя страница (
index.html
) вызывает внутренний код Node App Engine, чтобы получить список миниатюр изображений и их меток, посредством вызова AJAX по URL-адресу/api/pictures
. Домашняя страница использует Vue.js для получения этих данных. - Страница коллажа (
collage.html
) указывает на изображениеcollage.png
, в котором собраны 4 последних изображения. - Страница загрузки (
upload.html
) предлагает простую форму для загрузки изображения с помощью запроса POST по URL-адресу/api/pictures
.
Что вы узнаете
- Механизм приложений
- Облачное хранилище
- Облачный пожарный магазин
2. Настройка и требования
Самостоятельная настройка среды
- Войдите в Google Cloud Console и создайте новый проект или повторно используйте существующий. Если у вас еще нет учетной записи Gmail или Google Workspace, вам необходимо ее создать .
- Имя проекта — это отображаемое имя для участников этого проекта. Это строка символов, не используемая API Google, и вы можете обновить ее в любое время.
- Идентификатор проекта должен быть уникальным для всех проектов Google Cloud и неизменяемым (нельзя изменить после его установки). Cloud Console автоматически генерирует уникальную строку; обычно тебя не волнует, что это такое. В большинстве лабораторий кода вам потребуется указать идентификатор проекта (обычно он обозначается как
PROJECT_ID
), поэтому, если он вам не нравится, создайте другой случайный идентификатор или попробуйте свой собственный и посмотрите, доступен ли он. Затем он «замораживается» после создания проекта. - Существует третье значение — номер проекта , который используют некоторые API. Подробнее обо всех трех этих значениях читайте в документации .
- Затем вам необходимо включить выставление счетов в Cloud Console, чтобы использовать облачные ресурсы/API. Прохождение этой лаборатории кода не должно стоить много, если вообще стоит. Чтобы отключить ресурсы и не платить за выставление счетов за пределами этого руководства, следуйте инструкциям по «очистке», которые можно найти в конце лаборатории кода. Новые пользователи Google Cloud имеют право на участие в программе бесплатной пробной версии стоимостью 300 долларов США .
Запустить Cloud Shell
Хотя Google Cloud можно управлять удаленно с вашего ноутбука, в этой лаборатории вы будете использовать Google Cloud Shell , среду командной строки, работающую в облаке.
В Google Cloud Console щелкните значок Cloud Shell на верхней правой панели инструментов:
Подготовка и подключение к среде займет всего несколько минут. Когда все будет готово, вы должны увидеть что-то вроде этого:
Эта виртуальная машина оснащена всеми необходимыми инструментами разработки. Он предлагает постоянный домашний каталог объемом 5 ГБ и работает в Google Cloud, что значительно повышает производительность сети и аутентификацию. Всю работу в этой лабораторной работе можно выполнять с помощью простого браузера.
3. Включите API
App Engine требует API Compute Engine. Убедитесь, что он включен:
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
Затем вы можете перейти в каталог, содержащий интерфейс:
cd serverless-photosharing-workshop/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 с метаданными наших изображений,
- хранилище : для доступа к облачному хранилищу Google, где хранятся изображения,
- express : веб-фреймворк для Node.js,
- dayjs : небольшая библиотека для удобного отображения дат,
- bluebird : библиотека обещаний JavaScript,
- 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 для загрузки файлов. Теперь, когда файлы доступны в локальной файловой системе, вы загружаете изображения в корзину Cloud Storage. Наконец, вы перенаправляете пользователя обратно на главный экран приложения.
Перечисление фотографий
Пришло время показать ваши красивые фотографии!
В обработчике /api/pictures
вы просматриваете коллекцию pictures
базы данных Firestore, чтобы получить все изображения (миниатюры которых были созданы), упорядоченные по убыванию даты создания.
Вы помещаете каждое изображение в массив JavaScript с его именем, описывающими его метками (поступающими из API Cloud Vision), доминирующим цветом и удобной датой создания (с помощью 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>
</span>
{{ label }}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
Идентификатор div будет указывать Vue.js, что эта часть разметки будет динамически отображаться. Итерации выполняются благодаря директивам v-for
.
Изображения получают красивую цветную рамку, соответствующую доминирующему цвету изображения, как обнаружено Cloud Vision API, и мы указываем на миниатюры и полноразмерные изображения в ссылках и источниках изображений.
Наконец, мы перечисляем метки, описывающие картинку.
Вот код JavaScript для фрагмента 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
. Возвращенные данные затем привязываются к коду представления в разметке, которую вы видели ранее.
Просмотр фотографий
Из index.html
наши пользователи могут просматривать миниатюры изображений, нажимать на них, чтобы просмотреть полноразмерные изображения, а из collage.html
пользователи просматривают изображение collage.png
.
В HTML-разметке этих страниц изображение src
и ссылка href
указывают на эти три конечные точки, которые перенаправляют в облачное хранилище изображений, миниатюр и коллажей. Нет необходимости жестко прописывать путь в разметке 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 вы можете использовать функцию веб-предварительного просмотра для просмотра приложения, работающего локально:
Используйте CTRL-C
для выхода.
7. Развертывание в App Engine
Ваше приложение готово к развертыванию.
Настройка 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, как управление версиями и разделение трафика:
8. Проверьте приложение
Чтобы протестировать, перейдите по URL-адресу App Engine по умолчанию для приложения ( https://<YOUR_PROJECT_ID>.appspot.com/
), и вы увидите работающий интерфейс интерфейса!
9. Очистка (необязательно)
Если вы не собираетесь сохранять приложение, вы можете очистить ресурсы, чтобы сэкономить средства и стать в целом хорошим гражданином облака, удалив весь проект:
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
10. Поздравляем!
Поздравляем! Это веб-приложение Node.js, размещенное на App Engine, объединяет все ваши сервисы и позволяет вашим пользователям загружать и визуализировать изображения.
Что мы рассмотрели
- Механизм приложений
- Облачное хранилище
- Облачный пожарный магазин