首页 > 解决方案 > 使用预签名 URL 和 AWS Lambda 函数(使用 Angular 8、Node.js 和 Netlify)将文件上传到 AWS S3 存储桶

问题描述

最初,我有一个 Angular 8+ 应用程序,其中包含一个上传表单和一些生成 AWS 预签名 URL 的代码,这些 URL 用于将上传表单中选择的文件上传到 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
    };
  }
}

此代码工作正常,但我想删除从客户端(Angular 8+)生成预签名 URL 到 Node.js 应用程序中的 AWS Lambda 函数的责任。

所以我添加了一个新环境。我通过 Netlify 部署的 Node.js 实例,因此我可以通过 URL 轻松访问 AWS Lambda 函数,例如https://my-url.netlify.com/.netlify/functions/upload?file= ' + 文件。

这就是我的 AWS Lambda 函数在 Node.js 实例的 functions/upload.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)));
};

然后我可以向 Netlify URL 发出 HTTP POST 请求,在其中我将来自 Angular 8+ 客户端的文件作为参数传递给 Netlify URL,但是你不能真正将文件对象 [File object] 作为 URL 参数传递,所以我该如何进行?

可能有助于我弄清楚如何将 this.file 从 Angular 传输到 Node.js AWS Lambda 函数的问题:

  1. 在使用预签名 URL 时,而不是添加整个 this.file 对象,我可以只使用文件内容吗?如果是,是什么形式?

  2. 总的来说,您对这种方法有何看法?

  3. 是否有替代方法(要求:我仍然需要使用 Angular、Node.js 和 Netlify)

标签: node.jsangularaws-lambdanetlifypre-signed-url

解决方案


推荐阅读