serverless教程七:使用serverless开发AWS S3 API


在开发无服务器应用的时候,通过纯手工方式创建各种资源的话,效率会非常低,因此需要借助一些框架来提高开发效率。下面介绍如何在serverless中开发S3 API(Lambda)。

serverless教程
serverless教程

S3公共模块

增加文件api/common/s3/S3.js:

Javascript
const AWS = require('aws-sdk');
AWS.config.update({ region: "eu-west-1" });
const s3Client = new AWS.S3();

const S3 = {
    async get(params) {
        let obj = await s3Client.getObject(params).promise();

        if (!obj) {
            throw Error('Failed to get file from S3 bucket.');
        }
        if (params.Key.endsWith('.json') || params.Key.endsWith('.txt')) {
            obj = obj.Body.toString();
        }
        return obj;
    },
    async write(params) {
        const newObj = await s3Client.putObject(params).promise();

        if (!newObj) {
            throw Error('there was an error writing the file');
        }

        return newObj;
    },
};

module.exports = S3;

获取文件的Lambda handler

api/files/getFile.js:

Javascript
const Responses = require('../common/Responses');
const S3 = require('../common/s3/S3');

const bucket = process.env.bucketName;

exports.handler = async event => {
    if (!event.pathParameters || !event.pathParameters.fileName) {
        return Responses.HTTP_RESPONSE(400, { message: 'missing the fileName from the path' });
    }

    let fileName = event.pathParameters.fileName;

    const params = {
        Bucket: bucket,
        Key: fileName,
    };

    const file = await S3.get(params).catch(err => {
        console.log('error in S3 get', err);
        return null;
    });

    if (!file) {
        return Responses.HTTP_RESPONSE(500, { message: 'Failed to get file from S3 bucket.' });
    }

    return Responses.HTTP_RESPONSE(200, { file });
};

文本文件上传的Lambda handler

Javascript
const Responses = require('../common/Responses');
const S3 = require('../common/s3/S3');

const bucket = process.env.bucketName;

exports.handler = async event => {
    if (!event.pathParameters || !event.pathParameters.fileName) {
        return Responses.HTTP_RESPONSE(400, { message: 'fileName is missing in the path.' });
    }

    const fileName = event.pathParameters.fileName;
    const obj = JSON.parse(event.body);
    const params = {
        Bucket: bucket,
        Key: fileName,
        Body: JSON.stringify(obj),
    };

    const newObj = await S3.write(params).catch(err => {
        console.log('error in S3 write', err);
        return null;
    });

    if (!newObj) {
        return Responses.HTTP_RESPONSE(500, { message: 'Failed to upload file to S3.' });
    }

    return Responses.HTTP_RESPONSE(200, { newObj });
};

二进制(Base64)格式文件上传

对应的S3操作

TypeSript
async writeBinary(bucketName: string, fileName: string, buffer: Buffer, contentType: string) {
    const params = {
        Bucket: bucketName,
        Key: fileName,
        Body: buffer,
        ContentType: contentType,
    };
    const newObj = await s3Client.putObject(params).promise();
    if (!newObj) {
        throw Error('there was an error writing the file');
    }
    return newObj;
}

二进制大文件上传

当上传大文件的时候,就不太适合使用Lambda了,这个时候可以使用AWS S3的服务。在S3中,有一个方法叫做getPreSignedUrl,会生成一个上传URL,包含用户登录相关的信息。这个URL是有时效的。在客户端可以调用API获取一个这样的URL,然后发送文件上传请求到这个URL。

TypeScript
import { APIGatewayProxyEvent } from 'aws-lambda';
import Responses from './common/Responses';
import Identifier from './common/Identifier';
import S3 from './common/S3';

export const handler = async (event: APIGatewayProxyEvent) => {
  try {
    const userInput = JSON.parse(event.body || '{}');
    if (!userInput.mime || !userInput.fileExtension) {
      return Responses.HTTP_Response(400, {
        message: 'User input error, expect mime and fileExtension from the request body.',
      });
    }

    const s3Bucket = process.env.YOUR_BUCKET as string;
    const uuid = Identifier.long_identifer();
    const url = await S3.getPreSignedUrl(s3Bucket, `${uuid}.${userInput.fileExtension}`, userInput.mime);
    return Responses.HTTP_Response(200, {
      uploadUrl: url,
    });
  } catch (error) {
    console.log('error', error);
    return Responses.HTTP_Response(500, { message: 'Error occured while uploading file' });
  }
};

serverless.yml

Javascript
service: service-07-api-s3

provider:
  name: aws
  region: eu-west-1
  runtime: nodejs14.x
  lambdaHashingVersion: 20201221
  profile: default
  iamRoleStatements:
    - Effect: Allow
      Action:
          - s3:*
      Resource: '*'
  environment:
    bucketName: ${self:custom.bucketName}
  
custom:
  bucketName: lcoding-demo-bucket

resources:  
  Resources:
    TestBucket:
      Type: AWS::S3::Bucket
      Properties:
          BucketName: ${self:custom.bucketName}

functions:
  getFile:
    handler: api/files/getFile.handler
    events:
        - http:
            path: get-file/{fileName}
            method: GET
            cors: true 
  uploadFile:
    handler: api/files/uploadFile.handler
    events:
        - http:
            path: upload-file/{fileName}
            method: POST
            cors: true                   

plugins:
  - serverless-webpack
  
package:
  individually: true

文章作者: 逻思
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 逻思 !