发布于 2026-01-06 10 阅读
0

使用 AWS Amplify 创建简历上传工具

使用 AWS Amplify 创建简历上传工具

在本系列的第一部分中,我们学习了如何通过创建联系表单来发送电子邮件。在本文中,我们将更进一步,允许用户上传附件。

像Indeed.com这样的网站允许求职者上传简历,以便雇主浏览这些简历,看看候选人是否合适。

本文主要面向提交简历的求职者,但也可能适用于企业。

📝 因为本系列文章中的知识是随着每篇文章的推进而逐步积累的,所以在本文中,我们将花更多时间关注“如何做”,而只在适当的时候提及“为什么”

入门

我们首先下载入门项目。该项目包含我们的用户界面,但目前还没有与之关联的后端逻辑。

👉🏽点击此处获取入门文件

项目克隆到本地后,我们需要确保当前位于resume-uploader-starter正确的分支上。然后,我们就可以安装依赖项并运行应用程序了。

运行应用程序时,应该会看到以下屏幕:

// 应用截图
项目主页

这与上一个项目几乎完全相同,但是,点击“上传简历”按钮时,出现了一个上传文件的新选项:

带有文件上传功能的模态框

该项目的目标是允许用户上传文件,并将其保存在 AWS 中,同时通过电子邮件发送给已验证的收件人。

了解我们的后端

在深入代码之前,让我们花点时间了解一下我们的 AWS 服务将如何协同工作。

架构概述

以上面的截图为参考,当候选人进入我们的申请页面时,他们会被要求注册。

📝 我们希望用户进行身份验证,不仅是为了限制谁可以上传/下载文件,也是为了防止简历文件名相同(例如resume.pdf)。因此,我们将使用生成的用户 ID 作为简历文件名的前缀。

关于上面提到的内容,稍后再详细讨论 😉

应用程序的下一部分与上一篇文章非常相似:我们的 API 将数据发送到数据库,数据存储到表中后,会触发一个函数发送电子邮件。

这就引出了S3。

当我们的前端收到.pdf文件时,我们会将其存储在 S3 中。我们的 lambda 函数将负责获取正确的.pdf 文件并将其作为附件添加到电子邮件中。

这就是申请流程的大致内容。让我们开始把所有内容整合起来吧。

入门

在终端中,我们首先要确保位于项目的根目录。接下来,我们将安装一些 Amplify 依赖项,并初始化项目以使用 Amplify CLI:

npm i aws-amplify @aws-amplify/ui-react && amplify init
Enter fullscreen mode Exit fullscreen mode

依赖项安装完成后,系统会提示我们如何配置应用程序。

输入您选择的名称,然后n在询问您是否要使用默认配置初始化 Amplify 时**选择**。

这是因为我们想将构建目录从更改build为,out因为 NextJS 将把我们的网站构建为静态 HTML 文件。

当出现提示时,按 Enter 键接受默认选项;但当被要求输入“分发目录路径”时,键入并按Enter 键。

完成后,请确认您要使用的配置文件。整个流程应与以下屏幕截图类似:

放大配置项目

📝 如果您不小心接受了默认配置,或者以后想要更改配置,输入命令amplify configure project即可返回提示符。

项目配置完成后,我们开始添加服务。

添加身份验证

Amazon Cognito 用户池将存储我们的用户,而身份池将管理他们的权限。Amplify 可以通过命令行界面 (CLI) 为我们设置这些选项,并提供合理的默认设置(例如密码保护、TOTP 等)。

首先,我们在终端中输入以下命令:

amplify add auth
Enter fullscreen mode Exit fullscreen mode

系统会提示我们希望如何配置 Amazon Cognito。

针对我们的使用场景,我们将做出以下选择:

  1. 默认配置

  2. 用户名

  3. 不,我做完了。

整个流程应该如下图所示:

使用默认配置添加身份验证

通过 CLI 完成身份验证模板后,我们继续进行下一个服务。

添加 API

使用 AWS AppSync 设置 API 与上一节非常相似,只是增加了对候选人简历的引用。

回到终端,输入以下命令:

amplify add api
Enter fullscreen mode Exit fullscreen mode

对于接下来的提示,请选择以下选项:

  1. GraphQL

  2. 按 [回车] 选择默认名称

  3. Amazon Cognito 用户池

  4. 不,我做完了。

  5. 包含字段的单个对象

  6. 是的

生成的流程图应如下图所示:

放大添加 API 流程

选择最后一个选项中的“是”应该会在我们的编辑器中打开我们的架构文件。

请将内容替换为以下内容:

type Candidate 
  @model 
  @auth(rules: [{ allow: owner, operations: [create, update] }]) {
    id: ID!
    name: String!
    email: String!
    resumeFile: String!
    userIdentity: String!
}
Enter fullscreen mode Exit fullscreen mode

如上一篇文章所述,该@model指令将自动创建我们的 DynamoDB 表以及与之交互的相应 CRUDL 操作。

@auth指令规定,此 API 仅允许执行创建和更新操作。此外,这些操作仅限于当前登录用户。

这些字段对应于我们数据库中存储的内容—— id,,,nameemailresumeFile

我们还有一个userIdentity字段。当用户添加到 Cognito 时,系统会为其创建一个用户 ID。我们正在将此 ID 添加到 API 中,以便我们的 Lambda 函数(以及未来的雇主)能够访问简历。请注意,此 ID 与用户的用户名或密码无关 😉

接下来的两项服务才真正精彩起来。让我们深入了解一下!

添加 S3 存储桶

在我们的应用程序中,我们选择将简历存储在亚马逊简单存储服务 (S3) 中。这样做有以下好处:

  1. 减少存储在数据库中的有效载荷大小

  2. 我们无需费心向 Lambda 函数发送多部分表单数据。

  3. 我们有专门的邮件收发空间,而不是普通的邮件收件箱。

与目前为止的其他服务类似,我们将通过 CLI 创建 S3 存储桶。

首先,我们在终端中输入以下命令:

amplify add storage
Enter fullscreen mode Exit fullscreen mode

📝 Amplify 提供两种主要类型的存储:数据库和 S3 存储桶。

根据提示,我们将选择以下选项来配置 S3 存储桶:

  1. 内容

  2. 按 [回车] 接受默认设置

  3. 按 [回车] 接受默认设置

  4. 仅限已验证用户

  5. 使用空格键选择所有选项

完成后,生成的输出应如下图所示:

放大添加 S3 配置

S3 配置完成后,是时候将我们的服务连接起来了!

添加 Lambda 函数

我们的 lambda 函数有以下要求:

  • 由与我们的 API 关联的 DynamoDB 表触发

  • 可以访问我们刚刚创建的 S3 存储桶。

  • 拥有使用 SES 发送电子邮件的权限

Amplify 使我们能够在不访问 AWS 控制台的情况下完成所有这些操作。

首先,请在命令行界面 (CLI) 中输入以下命令:

amplify add function
Enter fullscreen mode Exit fullscreen mode

接下来,请进行以下选择:

  1. Lambda 函数

  2. "resumeFunc"

  3. NodeJS

  4. Lambda 触发器

  5. Amazon DynamoDB Stream

  6. 使用 API 类别 graphql @model 表

  7. 配置高级设置?是

  8. 输入“Y”以访问其他资源

  9. [使用空格键选择存储方式]

  10. [使用空格键选择我们的 S3 存储桶]
    选择“读取”

  11. “N”表示不按周期性计划调用

  12. 输入“N”表示不启用 Lambda 层

  13. 选择“Y”表示配置环境变量

  14. SES_EMAIL

  15. [请输入您可以访问的电子邮件地址]

  16. “我受够了”

  17. “N” 我们不需要配置密钥值

  18. “Y”我们现在想要编辑本地函数

📝 如果你觉得步骤太多,不妨试试手动操作!

📝 完成后,CLI 应该会提供一些它生成的环境变量:ENV、、REGION_YOUR_BUCKET_。暂时记下 bucket 变量,因为我们稍后会用到它。

生成的变量截图

与上一篇文章类似,每当我们的数据库发生更改事件(插入、修改或删除项目)时,都会触发此功能。

另外值得再次提及的是,在高负载情况下,DynamoDB 会同时处理批量更改。这就是我们进行迭代的原因event.Records

将 lambda 函数中生成的代码替换为以下代码:

const aws = require('aws-sdk')
const nodemailer = require('nodemailer')

const ses = new aws.SES()
const s3 = new aws.S3()
const transporter = nodemailer.createTransport({
  SES: { ses, aws },
})

exports.handler = async (event) => {
  for (const streamedItem of event.Records) {
    if (streamedItem.eventName === 'INSERT') {
      //pull off items from stream
      const filename = streamedItem.dynamodb.NewImage.resumeFile.S
      const candidateEmail = streamedItem.dynamodb.NewImage.email.S
      const candidateName = streamedItem.dynamodb.NewImage.name.S
      const candidateIdentity = streamedItem.dynamodb.NewImage.userIdentity.S
      //change this to match your bucket name👇🏽
      const RESUME_BUCKET = process.env.STORAGE_RESUMEBUCKET_BUCKETNAME 
      try {
        //get record from s3
        const resumeFile = await s3
          .getObject({
            Bucket: RESUME_BUCKET,
            Key: `protected/${candidateIdentity}/${filename}`,
          })
          .promise()

        //setup email with attachment
        const mailOptions = {
          from: process.env.SES_EMAIL,
          subject: 'Candidate Resume Submission',
          html: `<p>You can reach ${candidateName} at the following email: <b>${candidateEmail}</b></p>`,
          to: process.env.SES_EMAIL,
          attachments: [
            {
              filename,
              content: resumeFile.Body,
            },
          ],
        }

        //send email
        await transporter.sendMail(mailOptions)
      } catch (e) {
        console.error('Error', e)
      }
    }
  }
  return { status: 'done' }
}
Enter fullscreen mode Exit fullscreen mode

上述代码可以分为四个基本部分:

  1. 配置项目:这里我们将引入并设置相关的软件包。这个nodemailer软件包是一个方便的工具,我们稍后会安装。它可以简化发送带附件的电子邮件的操作。

  2. 从活动中获取我们需要的数据

  3. 正在获取相关简历文件。请注意,我们的文件已加密。

  4. 设置电子邮件并发送带有附件的电子邮件。

最后一步是授予我们的函数调用 SES 的权限。

上一节中,我们设置了SES。您可以从该资源中获取ARN,或者按照该文章中的步骤首次设置SES。

📝 如果您已安装 AWS CLI,则可以通过在终端中输入以下命令并单击发送到所提供电子邮件地址的验证链接来设置电子邮件:

aws ses verify-email-identity --email-address your-email@emai.com --region us-east-1 --profile=your-aws-profile
Enter fullscreen mode Exit fullscreen mode

获得我们创建的 SES 电子邮件的 ARN 后,我们需要打开我们函数的 CloudFormation 模板:

//from the root of your project
cd amplify/backend/function/YOUR_FUNC_NAME
Enter fullscreen mode Exit fullscreen mode

然后,打开以 . 结尾的文件-cloudformation-template.json

向对象添加一条语句lambdaexecutionpolicy

{
"Effect": "Allow",
"Action": "ses:SendRawEmail",
"Resource": "YOUR_SES_ARN"
}
Enter fullscreen mode Exit fullscreen mode

lambdaexecutionpolicy应该看起来像以下截图:

带有 ses sendRawEmail 权限的 lambda 策略

最后,我们将切换到srclambda 函数所在的目录,并安装 nodemailer 包:

// assuming we're still in the amplify/backend/function/ourFunction directory:

cd src && npm i nodemailer
Enter fullscreen mode Exit fullscreen mode

📝 除非我们在本地测试函数,否则无需安装此aws-sdk软件包。AWS 已在 Lambda 运行时中安装了此软件包。

配置我们的应用程序

所有服务配置连接完毕后,是时候将它们部署到 AWS 了。

在应用程序的根目录下,我们将运行以下命令:

amplify push
Enter fullscreen mode Exit fullscreen mode

这将显示一个表格,其中列出了所有已配置的服务。当询问是否继续时,请选择“是”(Y)并接受所有默认选项。

☕️ 这将把我们的后端资源部署到云端,为我们的 API 生成代码,并创建一个aws-exports包含我们的后端密钥的文件(自动添加到.gitignore)。

Amplify 状态 CLI 表

配置我们的前端

后端部署成功后,我们现在可以将其与前端连接起来。

添加_app.js以下代码片段,将我们的前端连接到 Amplify 后端:

import Amplify from '@aws-amplify/core'
import config from '../src/aws-exports'
Amplify.configure(config)
Enter fullscreen mode Exit fullscreen mode

下一步是让用户登录。除了让用户拥有自己的账户外,用户登录还能给我们带来以下好处:

  1. 我们掌握了谁在我们的 S3 存储桶中存储信息的信息。

  2. 我们可以控制谁有权查看和上传 S3 中的项目。

Amplify 提供了多种方式为我们的应用程序添加身份验证,具体取决于我们希望对过程拥有多少控制权。

对于这个项目,我们将采用最简单的方案,即使用高阶函数来完全管理身份验证流程。

index.js顶部部分修改为如下所示:

import { AppHeader } from '../components/AppHeader'
import { withAuthenticator } from '@aws-amplify/ui-react'

function HomePage() {
  return (
    <>
      <AppContainer>
        <AppHeader />
        <ResumeModal />
      </AppContainer>
    </>
  )
}

export default withAuthenticator(HomePage)

//rest of code...
Enter fullscreen mode Exit fullscreen mode

最后一步是添加将数据从前端发送到数据库和 S3 的实际代码。

添加ResumeForm.js以下导入语句:

import { API, Storage, Auth } from 'aws-amplify'
import { createCandidate } from '../src/graphql/mutations'
Storage.configure({ level: 'protected' })
Enter fullscreen mode Exit fullscreen mode

API 模块将与 AppSync 通信,存储模块将与 S3 通信,我们将引入在createCandidate推送 schema 时自动生成的 mutation。

请注意,我们将配置级别设置为protected

S3模块有三种模式:

  • 公开:所有文件存储在同一层级,所有用户均可访问。

  • 受保护:文件按用户的 Cognito 身份 ID 分隔。任何人都可以读取,但只有用户本人才能写入。

  • 私有:仅限指定用户访问。

📝 在某些情况下,文件公开可能是有意义的。但需要注意的是,同名文件会互相覆盖,这在我们的应用程序中是不可接受的。例如:user1 上传文件resume.pdf,user2 上传文件resume.pdf。因此,我们使用了protected.

在进行测试之前,最后一步是添加实际执行上传操作的代码。在同一个文件中,有一个名为 . 的函数handleResumeFormSubmit

让我们用以下内容替换注释和控制台语句:

const currentCredentials = await Auth.currentCredentials()
const fileKey = await Storage.put(
  formState.resumeFile.name,
  formState.resumeFile
)
const response = await API.graphql({
  query: createCandidate,
  variables: {
    input: {
      name,
      email,
      resumeFile: fileKey.key,
      userIdentity: currentCredentials.identityId,
    },
  },
})
Enter fullscreen mode Exit fullscreen mode

📝 因为我们将 S3 配置为protected,它会自动在我们的文件前添加protected/{COGNITO_IDENTITY_ID}/,但是,我们需要调用Auth.currentCredentials()才能获取 Cognito 用户身份 ID 以发送到我们的 lambda 函数。

确认所有文件都已保存后,重启应用程序并注册账号,我们应该就能上传.pdf文件并将其发送到我们在 SES 验证过的邮箱了!🎉

结论

坦白说,解释这个过程比实际操作的时间长得多😅

AWS Amplify 的强大之处在于其丰富的工具套件。本文深入探讨了其命令行界面 (CLI)。但实际上,它开箱即用,提供了 CI/CD 流水线、用户界面组件、管理界面以及更多功能。

感谢您抽出时间阅读本文!欢迎在评论区留言分享您的想法和疑问,或者在 Twitter 上关注我,获取更多关于 AWS Amplify 的见解!

下次再见🤖

文章来源:https://dev.to/focusotter/create-a-resume-uploader-using-aws-amplify-2e17