Какие технологии / библиотеки я изучу?

  • Firebase (Firestore, функции, хранилище, аутентификация Google, хостинг, безопасность базы данных, безопасность хранилища)
  • API машинного обучения Google Cloud Vision
  • Аутентификация Google с Firebase
  • Реагировать (создать-реагировать-приложение)
  • Бессерверные функции (с использованием функций Firebase)
  • Асинхронный / ожидание

Что я построю?

Демо: http://doppelganger-app.firebaseapp.com/

Код: https://bitbucket.org/bochrane/doppel-app

Дизайн: https://drive.google.com/file/d/1H_c7IBZG5Ggvns1MmPPQn5Tqik5w3Cfb/view?usp=sharing

В этом видео ниже показано, что мы будем строить

Вы создадите приложение для реагирования, используя функции firebase, которые загружают вашу фотографию, а затем возвращают список изображений людей, которые выглядят так же, как вы - в зависимости от того, кем Google считает вас похожим в любом случае :)

Как долго длится это руководство? 1–2 часа, если вы пишете код, примерно 20–30 минут, если вы просто хотите читать.

Я только что создал корпоративное приложение, которое заказывает строительное оборудование для строительной компании с помощью Firebase, и я настолько впечатлен тем, что оно может предложить, что хочу продемонстрировать некоторые вещи, которые благодаря Firebase стали возможны, на создание которых несколько лет назад потребовались бы месяцы.

Примечание. Если вы хотите, чтобы все работающие бессерверные функции взаимодействовали с Google Cloud Vision API, вам нужно будет ввести позже в руководстве действительную кредитную карту для Firebase - не волнуйтесь, хотя с вас не будут списаны средства до тех пор, пока вы используете огромное количество вызовов функций, о которых мы даже не поговорим в этом руководстве.

Итак, как мы создаем это приложение и откуда берутся эти двойные изображения?

Вот что мы будем использовать:

  • Google Cloud Vision API (API машинного обучения для изображений) предоставит предварительно обученную модель, анализируя любое загружаемое нами изображение и предоставив список изображений, которые выглядят точно так же, как загружаемое нами - довольно аккуратно!
  • Функции Firebase (да, в этом руководстве мы также узнаем о бессерверных функциях - мы скоро узнаем, что они собой представляют, если вы еще не встречали их)
  • Firebase Storage (для хранения загружаемых нами изображений) и Firebase Firestore (новая база данных реального времени без sql от Firebase)
  • React для внешнего приложения
  • Вход в Google (с использованием встроенной аутентификации Firebase с Google)

Я также включил файлы Sketch Design, а также интерактивный прототип Invision - я считаю, что с этими двумя инструментами намного проще спроектировать и создать прототип того, что я хочу построить заранее.



Итак, в этом руководстве мы затронем множество вещей, и я рад поделиться ими с вами. После создания этого приложения я больше всего понял, что возможность создавать крупномасштабные приложения с использованием API машинного обучения стала очень простой. Теперь к учебнику…

Мы разделим руководство на следующие части:

  1. Настройте приложение React (мы будем использовать приложение create-react-app)
  2. Настройте наше приложение Firebase
  3. Настроить Google Authentication
  4. Настройка хранилища Firebase, Firestore и добавления изображения
  5. Настройка функций Firebase, чтобы разрешить бессерверным функциям запускаться всякий раз, когда мы загружаем новое изображение
  6. Настройте Google Vision API в функциях Firebase, чтобы запрашивать похожие изображения из своего API.
  7. Протестируйте наш API Google Vision с нашим приложением
  8. Разверните в Firebase живое работающее приложение - ууу!

1. Настройте приложение React

Во-первых, убедитесь, что у вас установлен Узел (не ниже 6 версии).

Мы будем использовать Facebook create-react-app - хороший стартовый набор для создания приложений для реагирования.

npm install -g yarn
npx create-react-app my-doppelganger-app
cd my-doppelganger-app
yarn start

После запуска `npm start` вы должны увидеть этот экран

Затем создайте 3 папки внутри вашей папки src следующим образом:

mkdir src/features src/config src/helpers

Затем давайте настроим некоторые фиктивные компоненты для входа в систему, дома и альбома.

cd features
touch Login.js Home.js

Затем в src / features / Login.js добавьте пока фиктивный компонент:

src / features / Login.js

import React, { Component } from 'react';
export default class Login extends Component {
 render() {
  return (
   <div>this is my Login component</div>
  );
 }
}

И проделайте то же самое с компонентом Home:

src / features / Home.js

import React, { Component } from 'react';
export default class Home extends Component {
 render() {
  return (
   <div>this is my Home component</div>
  );
 }
}

Затем давайте настроим маршруты для приложения. Сначала мы установим response-router и react-router-dom:

yarn add react-router react-router-dom

В src / index.js мы добавим их и настроим наши маршруты:

src / index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import registerServiceWorker from './registerServiceWorker';
import { Redirect, Route, Switch } from 'react-router';
import { Router } from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory';
import Login from './features/Login';
import Home from './features/Home';
const customHistory = createBrowserHistory();
const Root = () => {
 return (
  <Router history={customHistory}>
   <Switch>
    <Route path="/login" component={Login} />
    <Route path="/app/home" component={Home} />
    <Redirect from="/" to="/login" />
   </Switch>
  </Router> 
 )
}
ReactDOM.render(<Root />, document.getElementById('root'));
registerServiceWorker();

Теперь мы должны увидеть компонент входа в систему в нашем браузере:

Затем мы можем проверить работу нашей маршрутизации, вручную перейдя к домашнему маршруту, чтобы убедиться, что они работают:

Теперь, когда наши маршруты работают, мы можем приступить к стилизации нашего компонента входа в систему:

Мы будем использовать этот png для кнопки.

Мы загрузим этот png-файл в Firebase Storage. Итак, сначала нам нужно будет войти в firebase с учетной записью Google, а затем создать новое приложение Firebase.

Затем перейдите в раздел «Хранилище» на левой панели навигации и нажмите «Начать работу». Затем вам будут показаны правила безопасности по умолчанию для вашей корзины хранилища - ниже показано, что безопасность по умолчанию - разрешать доступ к любому документу, пока пользователь вошел в систему. Для наших целей мы хотим разрешить некоторым изображениям разрешить пользователям, вышедшим из системы. чтобы просмотреть нашу кнопку входа в Google и фоновое изображение страницы входа.

Давайте изменим эти правила, чтобы позволить кому угодно просматривать любое изображение на данный момент (мы заблокируем это позже).

На вкладке «Правила» в хранилище измените свои правила следующим образом:

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write;
    }
  }
}

Затем загрузите изображение кнопки в хранилище.

Затем скопируйте URL-адрес загрузки только что загруженного изображения, щелкнув раскрывающееся меню Расположение файла после щелчка строки изображения.

Теперь мы можем установить наше фоновое изображение div, которое мы собираемся сделать для изображения Google, следующим образом:

src / features / Login.js

Выше мы включили вспомогательный метод loginWithGoogle, который использует метод firebaseAuth (). SignInWithRedirect (googleProvider) firebase дает нам из коробки, а googleProvider, переданный в этот метод, находится в config.constants.js, который является новым экземпляром. из firebase.auth.GoogleAuthProvider ().

Мы также показываем простой экран загрузки при входе в Google с помощью localStorage.

index.css

html {
 margin: 0;
 width: 100%;
}
body {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  max-width: 768px;
  margin: 0 auto;
  height: 100vh;
  width: 100%;
}
.login-button {
 height: 100px;
 width: 100%;
 position: fixed;
 bottom: 0;
 background: black;
 max-width: 768px;
 margin: 0 auto;
}
.login-container { 
 background: #FFC107;
 width: 100%;
 height: 100vh;
}
.button-text {
 position: relative;
 font-size: 25px;
 color: white;
 line-height: 50px;
 left: 90px;
}
.google-logo {
 height: 50px;
 float: left;
 background-size: contain;
 background-repeat: no-repeat; 
 margin: 25px 20px;
}
#root {
 width: 100%;
 height: 100vh;
}

Затем мы создадим фиктивные данные в домашнем маршруте, чтобы показать, как будет выглядеть наш канал doppelgänger.

Во-первых, мы воспользуемся реакцией-бутстрапом для нашей грид-системы.

yarn add react-bootstrap

Затем добавьте следующее в src / features / Home.js

src / features / Home.js

В Home.js выше вы увидите, что мы добавили React Bootstrap, включая компоненты Image, Grid, Row и Col. На данный момент мы также добавили набор фиктивных изображений и установили его в качестве исходного состояния «allPhotos».

Перед нашим последним оператором return мы перебираем allPhotos, которые мы установили в исходное состояние, используя встроенный метод «map» javascript. Каждый раз, когда мы просматриваем фотографию, мы создаем фрагмент HTML, который динамически включает в себя детали об этом изображении.

И добавьте этот css в index.css (это будет весь css, который нам понадобится для приложения)

index.css

Чтобы значки и response-bootstrap работали, нам нужно добавить cdn значков материалов, а также файлы css react-bootstrap.

index.html

<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
...
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
...

Теперь мы должны увидеть наш фиктивный список фотографий, а также нижнюю панель навигации:

Теперь наш index.html должен выглядеть так:

public / index.html

2. Настройте приложение Firebase.

Затем давайте настроим наше приложение Firebase внутри нашего приложения React.

Сначала перейдите в консоль firebase и нажмите «Обзор проекта» в левом верхнем углу навигации.

Затем нажмите кнопку «Добавить Firebase в свое веб-приложение» и скопируйте код, который появляется во всплывающем окне.

Создайте файл конфигурации в src / config / constants.js

src / config / constants.js

import firebase from 'firebase';
const config = {
    apiKey: "AIzaSyBBaJIkRtF3jgzFF7BC83G6nI9joZKxezg",
    authDomain: "doppelganger-app.firebaseapp.com",
    databaseURL: "https://doppelganger-app.firebaseio.com",
    projectId: "doppelganger-app",
    storageBucket: "doppelganger-app.appspot.com",
    messagingSenderId: "115334021919"
};
firebase.initializeApp(config);
export const googleProvider = new firebase.auth.GoogleAuthProvider();
export const firebaseAuth = firebase.auth;
export const db = firebase.firestore().settings({ timestampsInSnapshots: true });

Выше мы использовали детали конфигурации из нашего приложения firebase. Мы также добавили поставщика Google Auth из firebase, который позволит нам входить в систему через Google. И, наконец, мы настроили нашу базу данных Firebase - в этом случае мы будем использовать Firestore, который является новым хранилищем документов в реальном времени. Мы добавляем в настройку firestore параметр, который сохраняет метки времени create_at как метку времени firestore вместо обычного объекта даты.

Нам также потребуется установить firebase, а также интерфейс командной строки firebase (firebase-tools) следующим образом (я выбрал конкретную версию инструментов Firebase, чтобы приложение работало для вас так же, как и для меня, но не стесняйтесь использовать последнюю версию версия, если хотите):

npm install -g [email protected]

Мы также установим firebase локально в наше приложение:

yarn add [email protected]

Затем давайте настроим firebase локально в нашем терминале:

(примечание: я использую Oh-My-Zsh для своего терминала, что немного упрощает синтаксис, и я использую зеленую тему для стандартного приложения Терминал на Mac, просто зайдите в Терминал - ›Настройки -› выберите Grass и нажмите Default, чтобы использовать эту тему)

firebase login

Затем вы получите всплывающее окно входа в Google в новом браузере - продолжайте и аутентифицируйтесь с учетной записью gmail, которую вы использовали для регистрации в Firebase.

После того, как вы вошли в систему через терминал, мы можем настроить это приложение как приложение Firebase следующим образом:

firebase init

Вам будет предложено ответить на несколько вопросов, используйте эти ответы:

  1. Вы увидите 5 вариантов, чтобы отметить те, которые мы хотим, нажмите клавишу пробела и вверх и вниз, чтобы перейти к нужным параметрам. Нажмите пробел для Firestore, функций, хостинга и хранилища (нам не понадобится база данных, так как это старая база данных реального времени, и мы будем использовать базу данных Firestore)

2. Если мы вошли в систему, теперь мы можем найти проект, который мы создали в Firebase, в списке по следующему вопросу:

3. Нажмите Enter, чтобы принять значение по умолчанию для следующих шагов (эти изображения ниже выглядят похожими, но представляют собой разные вопросы по настройке для нашего приложения Firebase:

И, наконец, мы создали приложение Firebase - уууу!

Вы заметите, что добавлено множество новых файлов и папок. Очень кратко и обзор этих файлов следующий:

а. .firebaserc файл - это позволяет firebase знать, какое приложение использовать, и даст имя, подобное default, которое будет указывать на приложение firebase, которое вы назвали в своей консоли firebase.

б. firebase.json - это позволяет firebase знать, где найти файлы правил, которые мы можем определить в нашем приложении, и индексы базы данных, которые мы создаем (firebase предварительно индексирует нашу базу данных, чтобы ускорить фильтрацию - мы рассмотрим это позже в руководстве) а также там, где существует папка сборки, которую firebase развернет в Интернете - с помощью приложения create-response-app мы позже запустим команду npm run build, которая сгенерирует папку сборки с помощью webpack.

c. firestore.indexes.json - как уже упоминалось, здесь мы можем определить индексы нашей базы данных, чтобы сделать фильтрацию по различным коллекциям предварительно проиндексированной и намного быстрее

d. storage.rules - мы изменили наши правила хранения в консоли firebase онлайн, но как только мы развернем это приложение, они будут переопределены правилами хранения в этом файле, поэтому сейчас давайте изменим файл, чтобы отразить то, что мы сделали в консоли firebase . Просто убедитесь, что ваш файл storage.rules выглядит так:

storage.rules

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write;
    }
  }
}

е. Вы также увидите новую папку с именем functions. Вот где пойдут наши бессерверные функции - захватывающие времена! Мы также вернемся к ним позже в руководстве.

Итак, это новые файлы и папки, теперь мы настроим Firebase Storage, чтобы мы могли сохранять некоторые изображения.

ВАЖНО: прежде чем мы продолжим, если вы используете элемент управления исходным кодом git, такой как Github или Bitbucket, для сохранения своей работы, убедитесь, что вы добавили / functions / node_modules в свой файл .gitignore, как показано ниже:

.gitignore

...
# dependencies
/node_modules
/functions/node_modules
# testing
/coverage
...

3. Настройте аутентификацию Google.

Затем мы настроим возможность входить и выходить из нашего приложения с помощью Google, используя встроенный в Firebase провайдер аутентификации Google.

Сначала мы настроим наш вспомогательный файл auth.js:

touch src/helpers/auth.js

src / helpers / auth.js

import { firebaseAuth, googleProvider } from '../config/constants';
export function loginWithGoogle() {
 return firebaseAuth().signInWithRedirect(googleProvider);
}
export function logout() {
 return firebaseAuth().signOut();
}

Приведенный выше файл auth.js создаст 2 метода для входа в провайдер аутентификации Google Firebase, а также для выхода из системы.

Затем мы обновим наш файл Login.js следующим образом:

src / features / Login.js

import React, { Component } from 'react';
import { loginWithGoogle } from '../helpers/auth';
import { firebaseAuth } from '../config/constants';
const firebaseAuthKey = 'firebaseAuthInProgress';
const appTokenKey = 'appToken';
export default class Login extends Component {
constructor(props) {
        super(props);
        this.state = { splashScreen: false };
        this.handleGoogleLogin = this.handleGoogleLogin.bind(this);
    }
handleGoogleLogin() {
     loginWithGoogle()
     .catch(err => {
      localStorage.removeItem(firebaseAuthKey)
     });
// this will set the splashscreen until its overridden by the real firebaseAuthKey
     localStorage.setItem(firebaseAuthKey, '1');
    }
componentWillMount() {
// checks if we are logged in, if we are go to the home route
        if (localStorage.getItem(appTokenKey)) {
            this.props.history.push('/app/home');
            return;
        }
firebaseAuth().onAuthStateChanged(user => {
         if (user) {
          localStorage.removeItem(firebaseAuthKey);
          localStorage.setItem(appTokenKey, user.uid);
          this.props.history.push('/app/home')
         }
        })
    }
render() {
if (localStorage.getItem(firebaseAuthKey) === '1') 
   return <Splashscreen />;
  return <LoginPage handleGoogleLogin={this.handleGoogleLogin} />;
}
}
// this is the URL we copied from firebase storage
const loginButtonUrl = 'https://firebasestorage.googleapis.com/v0/b/doppelganger-app.appspot.com/o/google-icon-white.png?alt=media&token=ff891c5f-f2a4-441e-b457-d71b9b21762f';
const styles = {
 backgroundImage: `url(${loginButtonUrl})`
}
const LoginPage = ({ handleGoogleLogin }) => (
<div className="login-container">
  <div onClick={handleGoogleLogin} className="login-button">
   <div style={styles} className="google-logo">
    <span className="button-text">Sign In With Google</span>
   </div>
  </div>
 </div>
)
const Splashscreen = () => (<p>Please Wait Loading...</p>);

Мы добавили здесь много кода, так что давайте пройдемся по нему.

  • мы импортировали вспомогательный метод loginWithGoogle
  • в нашем конструкторе мы изначально скрываем заставку, а также привязываем функцию handleGoogleLogin, чтобы мы могли использовать ее в компоненте без необходимости связывать ее изнутри компонента.
  • функция handleGoogleLogin вызывает предварительно созданную функцию loginWithGoogle, которую мы импортировали (нас не интересует результат успеха, так как в любом случае он перейдет на домашнюю страницу в случае успеха, поэтому мы просто ловим ошибку)
  • мы используем метод жизненного цикла componentDidMount React, чтобы отслеживать, когда наш компонент монтируется, поскольку нам нужно знать, есть ли у нас зарегистрированный пользователь. Мы также используем встроенный в Firebase метод onAuthStateChanged, который проверяет, когда происходят такие вещи, как перезагрузка страницы, и возвращает текущего пользователя, вошедшего в систему.
  • мы используем localStorage, чтобы проверить, загружается ли логин, и в этом случае покажем компонент Splashscreen, в противном случае покажем компонент Login
  • мы используем функциональный компонент для LoginPage, и мы также передаем аргумент этому компоненту, который является методом handleGoogleLogin для использования, когда мы нажимаем кнопку входа в систему, чтобы у него был доступ к этому методу

Прежде чем мы протестируем это, нам нужно перейти в нашу консоль Firebase и включить Google в качестве параметра аутентификации для приложения.

Если вы уже пробовали аутентификацию Google в приложении и она застревает на экране-заставке, войдите в консоль в своем браузере и удалите только что установленный элемент localStorage:

В консоли браузера запустите это, если он завис на заставке:

localStorage.removeItem("firebaseAuthInProgress")

В консоли Firebase перейдите в раздел «Аутентификация» и нажмите «Настроить метод входа».

Консоль Firebase ›Аутентификация› Метод входа ›Google

Затем нажмите Google и включите переключатель, затем нажмите Сохранить.

Теперь вы должны получить экран аутентификации Google при нажатии кнопки входа в Google - ура!

Давайте теперь поработаем и "Выход".

В src / features / Home.js добавьте в код следующее:

src / features / Home.js

...
import { logout } from '../helpers/auth';
...
// inside of the constructor
constructor() {
   ...
   this.handleLogout = this.handleLogout.bind(this);
   ...
}
handleLogout() {
        logout()
        .then(() => {
            localStorage.removeItem(appTokenKey);
            this.props.history.push("/login");
            console.log("user signed out from firebase");            
        }.bind(this));
}

// then in the logout icon (the final Col in the Grid of icons) add a logout method like this:
...
<Col onClick={this.handleLogout} xs={4} className="col-bottom">
          <i className="bottom-icon material-icons">assignment_return</i>
</Col>
...

Итак, Home.js должен выглядеть так:

import React, { Component } from 'react';
import { Image, Grid, Row, Col } from 'react-bootstrap';
import { Link } from 'react-router-dom';
import { logout } from '../helpers/auth';
const appTokenKey = "appToken";
export default class Home extends Component {
constructor(props) {
        super(props);
const allPhotos = [
         {
          id: 'randomstringimadeup43454356546',
          url: 'http://fillmurray.com/200/200'
         },
         {
          id: 'randomstringimadeup43523526534565',
          url: 'http://fillmurray.com/200/200'
         },
         {
          id: 'randomstringimadeup433245234534',
          url: 'http://fillmurray.com/200/200'
         }                  
        ]
this.state = {
          allPhotos
        };
this.handleLogout = this.handleLogout.bind(this);
}
handleLogout() {
        logout()
        .then(() => {
            localStorage.removeItem(appTokenKey);
            this.props.history.push("/login");
            console.log("user signed out from firebase");            
        });
    }
render() {
// our doppelganger images
        const allImages = this.state.allPhotos.map(photo => {
return (
            <div key={photo.id}>
              <div style={{minHeight: '215px'}}>
                <i className="bottom-icon material-icons main-close">close</i>
                <Image style={{ width: '100%' }} src={photo.url} responsive />
              </div>
            </div>
          );
        })
return (
   <div>
    {allImages}
<Grid className="bottom-nav">
      <Row className="show-grid">
        <Col xs={4} className="col-bottom">
            <Link to="/app/album"><i className="bottom-icon material-icons">collections</i></Link>
        </Col>
        <Col xs={4} className="col-bottom">
            <i className="bottom-icon material-icons">camera_alt</i>
        </Col>
        <Col onClick={this.handleLogout} xs={4} className="col-bottom">
          <i className="bottom-icon material-icons">assignment_return</i>
        </Col>
      </Row>
    </Grid>
</div>
  );
 }
}

Теперь вы должны иметь возможность входить в систему и выходить из приложения с помощью Google auth - отличная работа - и вы знаете, что ...

4. Настройка Firebase Storage, Firestore и добавление изображения.

В этом разделе мы будем нажимать среднюю кнопку на нашей нижней панели навигации, чтобы предложить нам загрузить изображение, которое будет сохранено в хранилище Firebase. Пойдем!

Теперь мы будем использовать пакет загрузки файлов под названием response-firebase-file-uploader, поэтому давайте сначала установим его:

yarn add react-firebase-file-uploader

После того, как мы его установим, добавьте FileUploader JSX в Home.js, где расположена кнопка средней панели навигации, как показано ниже:

Фрагмент JSX для FileUploader

Мы также создадим функцию handleUploadSuccess (которая, как мы видим, является функцией, которая запускается во фрагменте FileUploader выше, когда файл успешно загружен. Она вернет кучу деталей об изображении, которое только что было загружено в Firebase Storage, и мы сохраним этот URL-адрес изображения в новую коллекцию, которую мы собираемся создать, под названием «фотографии» в нашей базе данных Firebase Firestore (также называемой хранилищем документов).

Функция handleUploadSuccess будет выглядеть так:

handleUploadSuccess.js

Теперь наш файл Home.js после двух изменений, внесенных выше, должен выглядеть так:

src / features / Home.js

Давайте объясним метод handleUploadSuccess (или функцию - как бы вы ни называли ее), а также фрагмент JSX FileUploader.

  1. HandleUploadSuccess
  • Метод handleUploadSuccess запускается из свойства фрагмента jsx FileUploader под названием «onUploadSuccess».
  • Мы используем синтаксис ES7 async-await, который является более приятным способом выполнения обещаний. Вместо «then-ing» и «catching» всего, мы теперь можем добавить слово «async» перед нашими функциями и поставить слово «await» перед всем, что мы обычно хотели бы добавить .then »к. Это отлично подходит для очистки нашего кода и не дает повсюду обещаний.
  • С синтаксисом async-await теперь мы можем проверять наличие ошибок с помощью блока try-catch. Блок try обертывает весь наш код async-await, и если есть ошибка в любом из фрагментов кода await, он перебрасывает ее в блок catch с возникшей ошибкой, которую в этом примере я назвал « ошибка »
  • Я также использую деструктурирующий синтаксис из ES6, который позволяет мне извлекать определенные части объекта, которые, как вы видите, я использовал для получения bucket и fullPath. значения в приведенном выше фрагменте. (bucket и fullPath - это метаданные на фотографии, которые описывают, где фотография находится в нашей корзине хранилища - важно знать, откуда получить доступ к нашей фотографии, чтобы отобразить ее в нашем приложении)
  • Используя информацию о фотографии и информацию о пользователе, мы затем создаем объект «newPhoto» для сохранения в нашей коллекции Firestore, который называется «фотографии».

Требуется действие:

Чтобы наше приложение работало и сохраняло эту новую фотографию не только в хранилище Firestore, но и добавляло ее как запись в нашу коллекцию фотографий в нашей базе данных, нам нужно настроить Firestore из консоли Firebase следующим образом:

Перейдите в консоль Firebase ›База данных

Вас спросят, какие настройки безопасности базы данных вы хотите - выберите «Начать в тестовом режиме» и нажмите «Включить». Это позволит читать и писать от неаутентифицированных пользователей, но, учитывая, что это тестовое приложение, мы пока оставим его таким.

Теперь, когда мы включили базу данных, давайте протестируем загрузку фотографии в приложение, нажав среднюю кнопку в нижней части навигации.

В случае успеха вы должны увидеть несколько журналов консоли, а также возможность заглянуть в хранилище и базу данных в Firebase и увидеть что-то вроде следующего:

Журналы консоли браузера:

Место хранения:

Коллекция фотографий Firestore:

Хорошая работа!

Последняя часть этого раздела - получить список фотографий, которые мы сохранили в нашей базе данных, и показать их на экране. В настоящее время мы используем фиктивный набор изображений (спасибо Филу Мюррею, но ваше время в нашем приложении истекло).

Теперь давайте воспользуемся методом жизненного цикла componentDidMount React, чтобы запросить изображения из нашей коллекции фотографий Firebase Firestore.

Чтобы получить начальный набор фотографий, мы будем использовать метод .get (позже для обновлений в реальном времени мы также будем включать метод onSnapshot из Firestore)

метод componentDidMount:

Теперь наше приложение должно иметь возможность показывать изображения, которые мы загружаем следующим образом (после перезагрузки, конечно, это то, что мы исправим дальше):

Вы заметите, что в нашем методе componentDidMount мы обращаемся к другому методу, называемому getInitial, который получит для нас все фотографии. Самое приятное в getInitial - это то, что он слушатель, а не одноразовый доступ к фотографиям. Поэтому каждый раз, когда мы вносим изменения в коллекцию фотографий, он автоматически обновляет и наш внешний список фотографий. Мы используем встроенный в Firestore метод onSnapshot, чтобы разрешать обновления в реальном времени - ууу!

Вы также заметите, что в методе getInitial мы проверяем, есть ли у нас зарегистрированный пользователь, прежде чем запрашивать фотографии. Мы делаем это с помощью встроенного метода аутентификации Firebase onAuthStateChanged, чтобы убедиться, что кто-то вошел в систему.

Попробуйте добавить еще один - и на этот раз он должен появиться в приложении, как только попадет в базу данных - хорошо!

Отличная работа! Мы приближаемся к тому, чтобы иметь работающее приложение-двойник. Давайте теперь запустим бессерверные функции, затем подключим их к Google Vision API - и готово.

Теперь ваш файл Home.js должен выглядеть так:

src / features / Home.js

5. Настройка функций Firebase, чтобы бессерверные функции запускались всякий раз, когда мы загружаем новое изображение.

Теперь мы собираемся использовать бессерверные функции, чтобы мы могли писать код на стороне сервера, который будет подключаться к Google Vision API, чтобы проверить, кто похож на нас.

Если вы еще не изучали бессерверное движение, на https://serverless.com/learn/ есть несколько отличных стартеров, которые помогут вам начать работу.

Короче говоря, бессерверное движение - это больше, чем просто написание бессерверных функций, оно позволяет разработчикам больше сосредоточиться на бизнес-логике, а не на инфраструктуре и настройке. Serverless - это немного вводящее в заблуждение название, потому что все еще есть серверы, на которых работает ваше приложение. Просто как разработчик много заботится об этой настройке и масштабировании (во всяком случае, на первых порах бессерверный режим обладает большой мощностью, но также имеет свои собственные скрытые подводные камни, такие как мониторинг и холодный запуск, которые улучшаются с каждым днем).

Мне нравится думать о бессерверных функциях как о любой внутренней функции, которую мы могли бы написать. Просто у каждой из этих функций есть свой маленький сервер, ожидающий запуска.

Когда вы запускаете эти функции, они запускают свои собственные мини-контейнеры, обычно на время от нескольких миллисекунд до 6 секунд, а иногда и дольше, в зависимости от вашей функции. Как только вы закончите выполнение этой функции, мини-контейнер снова выключится. Вы платите за объем используемой памяти и миллисекунды времени выполнения. Вы могли слышать, как люди говорят о бессерверных функциях как о Lambdas, учитывая, что AWS Lambda была одним из первых предложений (хотя и не первым), сделавшим бессерверные функции доступными для разработчиков).

Функции Firebase - это еще одна бессерверная функция, предлагаемая с использованием облачной платформы Google.

Хорошо, хватит о бессерверных функциях Бен, давай просто напишем ...

Погодите, почему мы снова пишем бессерверную функцию?

Потому что мы хотим поговорить с API Google Vision, чтобы предоставить нам список изображений, которые выглядят так же, как любое изображение, которое мы загружаем в нашу коллекцию фотографий.

Итак, поехали ...

Функции Firebase

Если вы еще не видите папку функций в своем основном приложении, давайте настроим ее с помощью папки функций, как показано в документации по функциям firebase https://firebase.google.com/docs/functions/get-started. (Если вы следовали инструкциям намного раньше в этом руководстве и запросили функции в настройке, это не применимо)

firebase init functions

Затем мы должны увидеть папку с именем functions и внутри нее файл с именем index.js.

Мы пока мало что увидим в этом файле index.js, но именно здесь мы можем написать все наши бессерверные функции, которые будут развернуты на платформе Google Cloud.

В нашем приложении есть package.json. В нашей папке функций также есть собственный package.json, который снова является отдельным и представляет собой модули узлов, которые нам понадобятся для нашего кода на стороне сервера, который мы собираемся написать. Давайте настроим модули узлов, которые нам понадобятся для наших бессерверных функций:

cd functions

Затем добавьте необходимые модули узла, в том числе модуль узла API Google Cloud Vision.

yarn add firebase-functions firebase-admin promise @google-cloud/vision

Затем установите все модули, которые уже были там, с новыми:

yarn

Мы добавим библиотеку обещание, поскольку все функции требуют, чтобы они были обещаны (клянусь, это слово?).

Затем в functions / index.js мы настроим наш файл бессерверных функций:

functions / index.js

Выше мы видим, что импортировали только что установленные модули, а также инициировали нашу базу данных Firestore, которая нам понадобится для обновления изображений, которые мы получаем от Google Vision API.

Затем давайте напишем функцию под названием addSimilarImages, которая будет добавлять похожие изображения из Google Vision API в список URL-адресов изображений, которые мы будем сохранять для фотографии, которую мы только что сохранили в Firestore.

Firestore имеет отличную функцию, которая позволяет прослушивать события в базе данных. Ниже вы увидите, как это используется с помощью метода functions.firestore.document (‘photos / {document}’)
.onCreate
следующим образом:

functions / index.js (полная версия с настройкой и функцией в ней)

Выше вы можете увидеть следующее:

  • Firestore использует прослушиватель onCreate, который позволяет вызывать функцию, когда происходит такое событие, как добавление новой фотографии в коллекцию.
  • Синтаксис позволяет нам проверять наличие любого документа в коллекции фотографий, добавляя .document (‘photos / {document}
  • snap возвращает снимок данных, которые были добавлены, в данном случае фотодокумент, который был добавлен в базу данных, и контекст позволяет нам узнать, какой тип метода был, например создавать, читать, обновлять, уничтожать среди других метаданных
  • мы создаем полный URL-адрес фотографии, который понимает API Google Vision, воссоздавая URL-адрес в стиле хранилища Google под названием photoUrl
  • затем мы возвращаем обещание после перехода к экземпляру Google Vision (visionClient) и использования метода webDetection (в этом API есть еще несколько методов, таких как labelDetection, faceDetection, чтобы назвать пару, которую вы могли бы хотели бы использовать вместо этого, которое вы можете найти здесь https://cloud.google.com/vision/docs/all-samples
  • В этом руководстве мы используем метод webDetection, который выбирает изображения, атрибуты которых аналогичны загруженному (которое мы назвали photoUrl).
  • Мы помещаем этот photoUrl через Vision API, и он возвращает список URL-адресов изображений - нас интересуют визуально похожие изображения.
  • Мы создаем массив этих визуально похожих изображений и обновляем документ в коллекции фотографий с помощью изображений похожих изображений.

ПРИМЕЧАНИЕ. Прежде чем описанная выше функция будет работать для нас, теперь нам нужно включить Google Cloud API в нашей учетной записи.

6. Настройте Google Vision API в функциях Firebase, чтобы запрашивать похожие изображения из его API

Теперь мы подходим к действительно захватывающей части, где мы будем использовать службу машинного обучения, чтобы помочь нам понять, кто еще в мире похож на нас, во всяком случае, согласно Google.

Шаг 1. Перейдите в консоль Google Cloud здесь https://console.cloud.google.com/ и войдите в систему (не волнуйтесь - у вас уже есть учетная запись - вам просто нужно войти при использовании той же учетной записи Gmail, что и ваша учетная запись Firebase)

Шаг 2. Нажмите "Выберите проект", затем нажмите "Новый проект".

Шаг 3. Назовите свой проект (я просто выбрал то же имя, что и мой проект firebase, но ничего страшного, если оно другое)

Шаг 4. Выберите только что созданный проект (здесь есть одна хитрость)

После того, как вы создали проект, мы снова возвращаемся на главную страницу консоли, но в левом верхнем углу не выбран проект, который мы только что создали, поэтому нам нужно выбрать его.

Вы могли бы подумать, что это легко, но нажатие кнопки «Выбрать проект», если у вас уже есть предыдущий проект, может не отображать ваш недавно созданный проект в списке - вам нужно будет щелкнуть вкладку Все проекты рядом с последними Вкладка "Проекты", чтобы найти его.

Шаг 5. Включите Google Vision API

Перейдите в панель управления API и службами - из консоли перейдите в раздел API и службы ›Панель управления, как показано ниже.

Затем нажмите Включить API и службы, как показано ниже.

Затем введите «видение» в поиске на этой странице ниже.

И нажмите Vision API.

Наконец, нажмите Включить, как показано ниже.

Вауза - в этом было что-то особенное! Теперь мы готовы приступить к использованию предварительно обученного API машинного обучения - ууу!

7. Протестируйте наш API Google Vision с нашим приложением и добавьте похожие изображения в наше приложение.

Вот и он - момент, над которым мы работали так долго, как долго вы читали это в автобусе, в поезде, дома на диване или на работе :)

Чтобы проверить, сработало ли это, нам нужно будет загрузить изображение, а затем посмотреть журналы функции Firebase, чтобы узнать, добились ли мы каких-либо успехов. Вы заметите, что я намеренно оставил в функции несколько журналов консоли, чтобы мы могли видеть, какие данные мы возвращаем.

Пойдем!

Во-первых, нам нужно развернуть наши функции в firebase, чтобы он распознал функции, которые мы только что написали. Мы пока не будем развертывать интерфейс, только наш серверный код.

Чтобы развернуть функции Firebase:

Из домашнего каталога приложения в терминале:

firebase deploy --only functions

Это только развернет папку функций.

Надеюсь, вы увидите такое подтверждение:

Если вы получаете какие-либо ошибки, просто убедитесь, что вы вошли в firebase, используя выход из firebase, затем войдите в firebase (затем войдите в систему с правильной учетной записью gmail) и запустите firebase, используя значение по умолчанию (которое находится в вашем файле .firebaserc - это больше всего вероятно, вы не изменили это, поэтому вы должны увидеть там ключ по умолчанию). Убедитесь также, что вы запустили yarn в папке функций, так как модуль должен быть установлен перед развертыванием в Firebase. И если вы получите сообщение об ошибке, подобное верхней части изображения выше, просто попробуйте еще раз. Магия.

Для работы Cloud Vision необходимо добавить платежный аккаунт

Как я уже упоминал в начале руководства, если вы хотите увидеть, как работают ваши функции, нам нужно будет добавить действительную кредитную карту в вашу консоль Firebase. Однако не волнуйтесь, уровень бесплатного пользования очень велик в месяц, прежде чем Google начнет взимать с вас цент. Вы можете ознакомиться с ценами на Firebase здесь и с ценами на Cloud Vision здесь. Короче говоря, нам нужно будет иметь более 125 тысяч вызовов функций в день, или размещать более 5 ГБ фотографий, или выполнять более 20 тысяч операций записи в коллекцию фотографий в день, или анализировать более 1000 изображений в месяц. в Google Cloud Vision, чтобы начать заряжать.

В левом нижнем углу консоли Firebase нажмите «Обновить», затем выберите план Blaze Pay as you go.

Наконец, откройте страницу функций в консоли Firebase и проверьте, все ли прошло успешно ...

Вы можете видеть выше, наша функция, которую мы назвали addSimilarImages, теперь находится в списке функций в нашей консоли firebase.

Отлично - теперь давайте проверим нашу функцию, загрузив изображение, а затем проверим журналы этой бессерверной функции, чтобы узнать, что произошло ...

Я только что загрузил фото. Теперь давайте нажмем View Logs, как показано ниже, в консоли Firebase Functions:

Если с нашей загрузкой фотографий все идет хорошо, в журналах функций вы должны увидеть что-то вроде этого:

Вы можете видеть выше, что на выполнение функции потребовалось 5553 мс, и был получен список похожих изображений из Google Cloud Vision API - если вы внимательно посмотрите журналы выше, Google предлагает мне выглядеть как некто по имени Эдвин из команды Stripe!

Последняя часть урока - показать эти похожие изображения в интерфейсе нашего приложения. Посмотрим, на кого мы похожи по версии Google…

Во-первых, мы собираемся добавить несколько загрузочных спиннеров, чтобы пользователь знал, что мы ждем, пока некоторые изображения-двойники вернутся из Google Vision API. Воспользуемся реактивными прядильщиками:

Из корня вашего приложения в терминале запустите:

yarn add react-spinners

Затем мы импортируем это в начало Home.js, а также импортируем Modal из react-bootstrap. Мы будем использовать модальное окно, когда кто-то щелкнет изображение двойника, которое откроет модальное окно с изображением в нем.

...
import { Image, Grid, Row, Col, Modal } from 'react-bootstrap';
...
import { ScaleLoader } from 'react-spinners';

Мы собираемся внести кучу небольших изменений, о которых я расскажу ниже. В конце изменений вот как должен выглядеть ваш файл Home.js:

src / features / Home.js

Давайте пробежимся по нашим дополнениям:

  • Мы добавили ряд загрузчиков, чтобы показывать, когда мы только что загрузили изображение и ждем, пока изображения двойников не вернутся из Google Cloud Vision. Мы перебираем массив «похожих изображений», который прикреплен к фотодокументу в функции firebase в index.js, которую мы создали ранее.
  • Мы также добавили список двойников под каждым изображением, на котором есть похожие изображения, и использовали CSS для создания горизонтального скроллера для этих изображений.
  • Мы также добавили модальное окно, чтобы при нажатии на изображение двойника мы могли видеть увеличенное изображение.
  • У нас также есть обработчик удаления в верхнем левом углу каждого изображения, чтобы мы могли удалить каждое изображение.
  • Мы добавили оператор where в наш список изображений, чтобы видеть только те изображения, которые мы загрузили. В firebase мы можем сделать это во внешнем интерфейсе, используя .where (‘userId’, ‘==’, user.uid) в нашем вызове коллекции. В следующем разделе мы также добавим правила Firestore на бэкэнд, которые добавят безопасность для репликации того, что у нас есть в интерфейсе.
  • Мы также добавили небольшую вспомогательную функцию, чтобы проверить, находимся ли мы на мобильном устройстве, поскольку у меня не было времени исправить проблему вращения при загрузке с мобильного устройства - обходной путь - повернуть ваше устройство в альбомную ориентацию, что позволяет изображению отображаться правильно. при просмотре его на рабочем столе
  • Мы также добавили часть состояния imageRef - это становится важным для настройки правил безопасности в хранилище Firebase позже - это позволит нам сопоставить пользователя, загружающего изображение, в определенное расположение файла.

Теперь у нас есть работающее приложение - по крайней мере, локально. Давайте развернем эту штуку в Интернете и закончим эту статью!

8. Разверните на Firebase живое работающее приложение - ууу!

Заключительная часть - развертывание!

Шаг 1. Добавьте правила безопасности перед развертыванием

В ваш файл firestore.rules мы добавим следующее, чтобы только вы могли читать, писать и удалять свои фотографии.

firestore.rules

storage.rules

Каждый раз, когда мы загружаем изображение, мы сохраняем путь для включения идентификатора авторизации пользователя (также известного как userId).

Если мы попытаемся запросить загрузку изображения без входа в систему или другой зарегистрированный пользователь попытается загрузить фотографии, которые ссылаются на ваш userId, загрузка будет отклонена следующим образом:

Приведенные выше правила - хороший способ достижения контроля доступа на основе атрибутов - простыми словами, возможность определять, какие типы действий (создание, чтение и т. Д.) Пользователям разрешено делать и в каких коллекциях (например, коллекция фотографий). Правила также являются функциональными, то есть вы можете создавать функции вне блока service cloud.firestore и обращаться к ним тоже внутри него.

Для наших целей выше мы говорим, что доступ предоставляется только коллекциям фотографий, и для любой фотографии в коллекции фотографий мы разрешим чтение или удаление, если ваш идентификатор пользователя из аутентификации firebase совпадает с атрибутом userId в фото документ, который мы сохранили.

Аналогично для записи объектов вы можете сохранять документы в базу данных только в том случае, если ваш идентификатор пользователя совпадает с атрибутом userId в полезной нагрузке документа коллекции фотографий. «Довольно мило», - подумал я.

Шаг 2. Создайте версию сборки нашего приложения для реагирования

В вашем терминале запустите:

yarn build

Если он построен правильно, вы должны увидеть такое сообщение:

** Важно **

В вашем файле firebase.json в корне вашего приложения мы сообщим firebase, в какой папке найти приложение для реагирования. Вы заметите, что для размещения нашего приложения для реагирования была создана папка сборки, поэтому нам нужно изменить « public »на« build », например:

firebase.json

{
  "firestore": {
    "rules": "firestore.rules",
    "indexes": "firestore.indexes.json"
  },
  "hosting": {
    "public": "build",
    "rewrites": [ {
      "source": "**",
      "destination": "/index.html"
    }],    
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  },
  "storage": {
    "rules": "storage.rules"
  }
}

Шаг 2. Разверните полное приложение в Firebase

В вашем терминале запустите:

firebase deploy

Это развернет как ваше интерфейсное приложение, так и папку функций firebase.

Если повезет, ваше приложение развернуто, и у вас есть работающее приложение-двойник! Ву!



Молодцы, что зашли так далеко! Мы многое рассмотрели.

Подводя итог, вы только что научились:

  • создать приложение для реагирования и использовать response-router-dom
  • правила безопасности для базы данных Firebase Firestore и хранилища Firebase
  • развертывание приложения react и firebase
  • взаимодействие с API машинного обучения с помощью Google Cloud Vision
  • бессерверные функции с использованием функций Firebase для взаимодействия с Google Cloud Vision API
  • и, самое главное……
  • Вы узнали, кем вы выглядите в Google!

Не стесняйтесь оставлять любые комментарии, вопросы и любые улучшения, которые вы хотели бы видеть в этом руководстве.

Если у вас возникнут дополнительные вопросы, обращайтесь ко мне через LinkedIn.