Предположим, вы хотите разрешить пользователям вашего приложения выгружать или скачивать из корзины S3 без необходимости создавать сложные роли IAM и разрешения на AWS S3.

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

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

Но как добиться того же результата, если у вас нет настраиваемой серверной части?

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

Облачные функции Google спешат на помощь

Облачные функции Google (также известные как функции Firebase) - это дополнение к службам, предоставляемым Firebase, которое позволяет вам писать функции Javascript для выполнения в облаке, вызывая их из внешнего (или внутреннего) приложения.

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



Мы хотим достичь следующего:

Пример настройки

В этой статье я предполагаю, что:

  • Вы уже настроили интерфейсное приложение
  • Вы создали проект Firebase
  • У вас есть учетная запись AWS и вы создали корзину S3 с разрешениями по умолчанию (вы единственный владелец и единственный, у кого есть доступ)

Если вы этого не сделали, в Интернете есть множество статей, в которых объясняется, как это сделать. Вот некоторые:

Затем нам нужно интегрировать Google Cloud Functions в наш проект. В этой статье хорошо объясняется, как это сделать, поэтому я не буду повторять ее здесь:



Настройка CORS для S3

Чтобы отправить HTTP-запрос на предварительно подписанный URL-адрес из внешнего интерфейса, нам необходимо настроить нашу корзину S3 с правильной политикой CORS. Если мы этого не сделаем, наш браузер или приложение не сможет отправить запрос.

В этой статье хорошо объясняется, как это сделать:



Вот пример политики CORS, которую вы можете использовать:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

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

Написание наших облачных функций

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

functions/
  index.js
  node-modules/
  package-lock.json
  package.json

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

Чтобы серверная часть могла успешно обращаться к конечным точкам сервисов AWS, в нашем случае S3, нам нужно создать для него IAM, а затем получить относительные ключи API.

Я снова ссылаюсь на внешнюю статью, в которой объясняется, как это сделать:



По сути, убедитесь, что ваша учетная запись IAM использует политику, которая предоставляет ей полный доступ к ресурсу S3. Вы можете легко использовать политику AmazonS3FullAccess.

Если у вас уже есть учетная запись IAM, просто убедитесь, что у нее есть доступ к ресурсам S3.

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

Это выглядит так:

aws_access_key_id = YOUR_ACCESS_KEY_ID
aws_secret_access_key = YOUR_SECRET_ACCESS_KEY

Теперь мы можем установить AWS S3 SDK в нашу установку облачных функций, чтобы мы могли делать запросы к S3 из облачных функций. Перейдите в папку functions, созданную инструментами firebase cli, и запустите:

npm add aws-sdk

Пример функции загрузки

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

Откройте index.js и удалите существующий код. Затем напишите нашу функцию:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const AWS = require('aws-sdk');
exports.getS3SignedUrlUpload = functions.https.onCall((data, context) => {
  AWS.config.update({
    accessKeyId: "YOUR_ACCESS_KEY_ID",
    secretAccessKey: "YOUR_SECRET_ACCESS_KEY",
    region: "YOUR_AWS_REGION" // (e.g. :)"eu-west-1"
  });
  var s3 = new AWS.S3();
const s3Params = {
    Bucket: data.S3BucketName,
    Key: data.key,
    Expires: 600, // Expires in 10 minutes
    ContentType: data.contentType,
    ACL: 'public-read', // Could be something else
    ServerSideEncryption: 'AES256'
  };
return s3.getSignedUrl('putObject', s3Params);
});

Давайте разберемся, что здесь происходит.

Сначала мы импортируем необходимые модули Node.js: нам нужны как модули Firebase Admin, так и модуль AWS SDK, чтобы мы могли связываться с конечной точкой S3.

exports.getS3SignedUrlUpload = functions.https.onCall((data, context) =>

указывает имя функции и ее тип. Вы можете определить как облачные функции HTTP, так и «функции приложения», которые требуют меньше настроек и проще в использовании. В этом случае мы используем функции приложения. Вы можете прочитать о них подробнее здесь:



data - это структура JSON, содержащая следующие поля:

data {
  S3BucketName: "my-bucket-name",
  key: "file-to-upload-name",
  contentType: "mime-content-type", // E.g.: "application/zip",
}

которые используются для настройки предварительно подписанного URL. Он создается во внешнем интерфейсе и отправляется вместе с запросом функции Google Cloud. Таким образом, мы можем иметь форму во внешнем интерфейсе, в которой указывается имя файла для загрузки, какой файл для загрузки и т. Д.

  AWS.config.update({
    accessKeyId: "YOUR_ACCESS_KEY_ID",
    secretAccessKey: "YOUR_SECRET_ACCESS_KEY",
    region: "YOUR_AWS_REGION" // (e.g. :)"eu-west-1"
  });
  var s3 = new AWS.S3();

Здесь мы настраиваем AWS SDK с учетными данными, полученными из роли IAM для серверной части, а затем создаем объект, который позволяет нам взаимодействовать с S3 API.

  const s3Params = {
    Bucket: data.S3BucketName,
    Key: data.key,
    Expires: 600, // Expires in 10 minutes
    ContentType: data.contentType,
    ACL: 'public-read', // Could be something else
    ServerSideEncryption: 'AES256'
  };

Затем мы создаем объект JSON с параметрами, необходимыми для запроса предварительно подписанного URL-адреса от S3. Некоторые из них не являются обязательными, но помогают уточнить, какой тип файла мы загружаем и какой уровень шифрования использовать. На этой странице объясняются различные доступные параметры:



Не стесняйтесь указывать дополнительные параметры и изменять содержимое структуры данных JSON.

return s3.getSignedUrl('putObject', s3Params);

Наконец, мы отправляем запрос в S3 API на получение предварительно подписанного URL-адреса и возвращаем URL-адрес клиенту.

Пример использования внешнего интерфейса

Теперь давайте используем наш предварительно подписанный URL-адрес во внешнем интерфейсе, чтобы загрузить файл в S3:

...
// Retrieve the Cloud Function
var getPost = functions.httpsCallable('getS3SignedUrlUpload');
// Call the cloud function
getPost(s3GetUrlParams).then(result => {
  // S3 pre-signed URL
  const options = {
          headers: {
            'Content-Type': 'application/zip',
            'x-amz-acl': 'public-read',
            'x-amz-server-side-encryption': 'AES256'
          }
   };
   // Run a HTTP PUT request to the pre-signed URL
   return axios.put(result.data, this.state.zipFile, options)
          .then(result => {...});
}

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

  1. Вызов функции облака
  2. Используйте возвращенный предварительно подписанный URL

Что, если я хочу загрузить файл из корзины S3?

Большая часть кода останется прежней.

В облачной функции вместо запроса URL-адреса "putObject" вы должны запросить URL-адрес "getObject":

return s3.getSignedUrl('getObject', s3Params);

а на стороне интерфейса вы отправляете HTTP-запрос GET, а не PUT.

Заключение

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

Следуйте за мной в Twitter: twitter.com/albtaiuti

Если вам понравилась эта статья, нажмите кнопку 👏 и поделитесь ею, чтобы помочь другим найти ее! Не стесняйтесь, чтобы оставить комментарий ниже.