Загрузка файлов в корзину AWS S3 с использованием предварительно назначенных URL-адресов и лямбда-функций AWS (с использованием Angular 8, Node.js и Netlify) - PullRequest
0 голосов
/ 24 октября 2019

Изначально у меня было приложение Angular 8+ с формой загрузки и некоторым кодом, который генерировал предварительно назначенные URL-адреса AWS, которые использовались для загрузки файла, выбранного в форме загрузки, в контейнеры AWS S3. Код выглядел примерно так:

const AWS = require('aws-sdk');

import { Component, OnInit, } from '@angular/core';
import { Router } from '@angular/router';
import { HttpClient } from '@angular/common/http';

import { environment } from '../../../environments/environment';

@Component({
  selector: 'app-upload-page',
  templateUrl: './upload-page.component.html',
  styleUrls: ['./upload-page.component.scss']
})

export class UploadComponent implements OnInit {
  private S3: any;
  private UPLOAD_FOLDER = 'test/';
  public file: File;

  constructor(private http: HttpClient) {}

  ngOnInit(): void {
    AWS.config.update({
      accessKeyId: environment.amazon.s3.secretAccessId,
      secretAccessKey: environment.amazon.s3.secretAccessKey,
      region: environment.amazon.s3.region,
      signatureVersion: environment.amazon.s3.signatureVersion
    });

    this.S3 = new AWS.S3();
  }

  // Triggered when clicking the upload button after attaching a file to <input type="file"/>
  public async onUpload(): Promise<void> {
    try {
      const params = this.buildUploadParams(environment.amazon.s3.bucketname, this.UPLOAD_FOLDER + this.file.name, this.file.type, 120);
      const preSignedGetUrl = this.S3.getSignedUrl('putObject', params);
      await this.upload(preSignedGetUrl, this.file); // this.file is the body
      this.file = null;
    } catch (error) {
      console.error(error);
    }
  }

  // Triggered when selecting a file
  public onChange($event: any): void {
    const files = $event.srcElement.files;
    this.file = files[0];
  }

  // Triggers the presigned url and attaches this.file to body
  private upload(url: string, body: any): Promise<any> {
    return new Promise((resolve, reject) => {
      this.http.put(url, body).toPromise()
        .then(res => resolve(res), (error: any) => reject(error));
    });
  }

  // Builds params required to generate presigned urls
  private buildUploadParams(bucket: string, key: string, contentType: string, expires: number): any {
    return {
      Bucket: bucket,
      Key: key,
      ContentType: contentType,
      ACL: 'bucket-owner-full-control',
      Expires: expires
    };
  }
}

Этот код работает нормально, но я хочу снять ответственность за генерацию предопределенных URL-адресов из клиента (Angular 8+) для функций AWS Lambda в приложении Node.js.

Итак, я добавляю новую среду. Экземпляр Node.js, который я развертываю через Netlify, чтобы я мог легко получить доступ к функциям AWS Lambda через URL-адреса, например, https://my -url.netlify.com / .netlify / functions / upload? File = '+файл.

Вот так выглядит моя лямбда-функция AWS в файле functions / upload.js в экземпляре Node.js:

// Below is the content of the upload.js file
const AWS = require('aws-sdk');
const request = require('request');

// AWS credentials
const S3_ACCESS_KEY_ID = 'MY_ACCESS_KEY_ID';
const S3_SECRET_ACCESS_KEY = 'MY_SECRET_ACCESS_KEY';
const BUCKET_NAME = 'MY_BUCKET_NAME';
const UPLOAD_FOLDER = 'test/';

AWS.config.update({
  accessKeyId: S3_ACCESS_KEY_ID,
  secretAccessKey: S3_ACCESS_ACCESS_KEY,
  region: 'eu-west-1',
  signatureVersion: 'v4'
});

const s3 = new AWS.S3();

function buildUploadParams(bucket, key, contentType, expires) {
  return {
    Bucket: bucket,
    Key: key,
    ContentType: contentType,
    ACL: 'bucket-owner-full-control',
    Expires: expires
  }
}

function upload(url, body) {
  return new Promise((resolve, reject) => {
    request({ method: 'PUT', url: url, body: body }, (error, response, body) => {
      if (error) {
        return reject(error);
      }

      if (response.statusCode !== 200) {
        return reject(body.error_description);
      }

      resolve();
    });
  });
}

function response (code, body) {
  return {
    'statusCode': code,
    'headers': { 'Access-Control-Allow-Origin': '*' },
    'body': body,
    'isBase64Encoded': false
  }
}

async function uploadFile(file) {
  const params = buildUploadParams(BUCKET_NAME, UPLOAD_FOLDER, file.type, 120);
  const preSignedGetUrl = s3.getSignedUrl('putObject', params);

  await upload(preSignedGetUrl, file);

  return 'Successfully uploaded file';
}

// AWS Lambda Function 
exports.handler = async (event, context, callback) => {
  // Should I pass file content with the query and get it like this: event.queryStringParameters['file']; 
  const file = event.queryStringParameters['file'];

  callback(null, response(200, uploadFile(file)));
};

Затем я могу сделать HTTP-запрос POST к Netlify. URL-адрес, по которому я передаю файл в качестве параметра от клиента Angular 8+ в URL-адресе Netlify, но вы не можете передать файловый объект [File object] в качестве параметра URL-адреса, поэтому как мне продолжить?

Вопросы, которые могут помочь мне понять, как перенести this.file из Angular в Node.js. Функции AWS Lambda:

  1. Вместо добавления всего объекта this.file при использовании предварительно назначенного URL-адреса,я могу просто использовать содержимое файла? Если да, в каких формах?

  2. Каково ваше мнение об этом подходе в целом?

  3. Существуют ли альтернативные подходы (Требование: я все ещенужно использовать Angular, Node.js и Netlify)

...