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

如何使用 AWS S3 预签名 URL 上传和下载文件 DEV 的全球展示挑战赛,由 Mux 呈现:展示你的项目!

如何使用 AWS S3 预签名 URL 上传和下载文件

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

本月我们新增了一位客户,需要跟踪分销商和医生的订单及销售情况。客户要求对S3存储桶进行私有化管理,仅允许使用预签名URL访问文件。因此,在本篇博客中,我将向您展示如何使用预签名URL在AWS S3存储桶中上传和下载文件,同时确保存储桶的私密性。

目录

先决条件

  • JavaScript 基础知识
  • AWS S3 存储桶的基本知识
  • 具备HTTP请求的基础知识
  • 具备 Node.js 和 Express.js 的基础知识

让我们把任务分解成更小的步骤。

  1. 设置后端
  2. 开发一个函数来生成 AWS S3 预签名 URL
  3. 配置 AWS S3 存储桶
  4. 将函数连接到 API 端点
  5. 设置前端
  6. 将前端连接到 API

步骤 1:设置后端

mkdir backend
cd backend
npm init -y
npm install express aws-sdk
touch index.js
Enter fullscreen mode Exit fullscreen mode

Windows 用户可以使用此方法type nul > index.js创建新文件。

// index.js
const express = require('express')
const app = express()
const AWS = require('aws-sdk')

app.listen(3000, () => {
  console.log('Server is running on port 3000')
})
Enter fullscreen mode Exit fullscreen mode

步骤 2:开发一个函数来生成 AWS S3 预签名 URL

// index.js
const express = require('express')
const app = express()
const AWS = require('aws-sdk')

const s3 = new AWS.S3({
    accessKeyId: process.env.AWS_ACCESS_KEY_ID, // Your AWS Access Key ID
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, // Your AWS Secret Access Key
    region: process.env.AWS_REGION // Your AWS region
    signatureVersion: 'v4', // This is the default value
})

const awsS3GeneratePresignedUrl = async (
  path,
  operation = 'putObject', // Default value is putObject, for get use getObject
  expires = 60
): Promise<string> => {
  const params = {
    Bucket: bucketName, // Bucket name
    Key: path, // File name you want to save as in S3
    Expires: expires, // 60 seconds is the default value, change if you want
  }
  const uploadURL = await s3.getSignedUrlPromise(operation, params)
  return uploadURL
}

app.listen(3000, () => {
    console.log('Server is running on port 3000')
})
Enter fullscreen mode Exit fullscreen mode

步骤 3:配置 AWS S3 存储桶

如果我们尝试将 API 连接到我们的函数,将会收到一个 CORS(跨域资源共享)错误。要解决此问题,我们需要配置 S3 存储桶以允许 API 访问。我们希望 API 能够发起 PUT 和 GET 请求。为此,我们需要按如下方式向 S3 存储桶添加 CORS 配置:

  • 打开 Amazon S3 控制台,网址为https://console.aws.amazon.com/s3/
  • 找到要配置的存储桶,然后点击它。
  • 点击标签Permissions
  • 向下滚动至该Cross-origin resource sharing (CORS)部分
  • 点击Edit并添加以下政策
[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "PUT",
            "GET",
            "HEAD"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": []
    }
]
Enter fullscreen mode Exit fullscreen mode
  • 点击Save changes
  • 另外,请确保已启用“阻止公共访问(存储桶设置)”以保持存储桶的私密性(可选)。

将 `<API URL>` 替换AllowedOrigins为您的 API URL 以提高安全性。或者,您也可以使用通配符*允许来自任何来源的访问。

步骤 4:将函数连接到 API 端点

我们将有两个端点,一个用于生成用于上传文件的预签名 URL,另一个用于生成用于下载文件的预签名 URL。

// index.js
const express = require('express')
const app = express()
const AWS = require('aws-sdk')

const s3 = new AWS.S3({
    accessKeyId: process.env.AWS_ACCESS_KEY_ID, // Your AWS Access Key ID
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, // Your AWS Secret Access Key
    region: process.env.AWS_REGION // Your AWS region
    signatureVersion: 'v4', // This is the default value
})

const awsS3GeneratePresignedUrl = async (
  path,
  operation = 'putObject', // Default value is putObject, for get use getObject
  expires = 60
): Promise<string> => {
  const params = {
    Bucket: bucketName, // Bucket name
    Key: path, // File name you want to save as in S3
    Expires: expires, // 60 seconds is the default value, change if you want
  }
  const uploadURL = await s3.getSignedUrlPromise(operation, params)
  return uploadURL
}

app.get('/generate-presigned-url', async (req, res) => {
  const { path } = req.query
  const uploadURL = await awsS3GeneratePresignedUrl(path, 'putObject', 60)
  res.send({ path, uploadURL })
})

app.get('/download-presigned-url', async (req, res) => {
  const { path } = req.query
  const downloadURL = await awsS3GeneratePresignedUrl(path, 'getObject', 60)
  res.send({ downloadURL })
})

app.listen(3000, () => {
    console.log('Server is running on port 3000')
})
Enter fullscreen mode Exit fullscreen mode

步骤五:设置前端

前端我使用的是 React.js。你也可以选择任何你喜欢的前端框架。安装后axios即可发送 HTTP 请求。

npx create-react-app frontend
cd frontend
npm install axios
Enter fullscreen mode Exit fullscreen mode

步骤 6:将前端连接到 API

// App.js
import React, { useState } from 'react'
import axios from 'axios'

export default function App() {
  const [uploadURL, setUploadURL] = useState('')
  const [downloadURL, setDownloadURL] = useState('')

  const generatePresignedURL = async (path, type) => {
    const response = await axios.get(
      `http://localhost:3000/generate-presigned-url?path=${path}`,
    )
    if (type === 'upload') {
      setUploadURL(response.data.uploadURL)
    } else {
      setDownloadURL(response.data.downloadURL)
    }
  }

  return (
    <div>
      <input type="file" onChange={(e) => uploadFile(e.target.files[0])} />
      <button onClick={() => generatePresignedURL('file1.txt', 'upload')}>
        Generate Upload URL
      </button>
      <button onClick={() => generatePresignedURL('file1.txt', 'download')}>
        Generate Download URL
      </button>
      {downloadURL && (
        <a href={downloadURL} download>
          Download file
        </a>
      )}
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

用例

  1. 从前端上传文件到 S3 存储桶,而无需暴露您的 AWS 凭证。
  2. 无需暴露您的 AWS 凭证,即可将文件从 S3 存储桶下载到您的前端。
  3. 无需创建 API 来处理文件上传,即可直接从前端将文件上传到 S3 存储桶。

奖励代码片段

// Code to get uploadUrl and put the file to the S3 bucket using fetch API.
const putFileToS3Api = async ({ uploadURL, file }) => {
  try {
    if (!file) throw new Error('No file provided')
    const res = await fetch(signedUrl, {
      method: 'PUT',
      headers: {
        'Content-Type': file.type ?? 'multipart/form-data',
      },
      body: file,
    })
    return res
  } catch (error) {
    console.error(error)
  }
}

const getUploadUrlApi = async ({ filename }) => {
  // Update the URL to your Graphql Endpoint.
  const res = await axios.get('http://localhost:3000/generate-presigned-url', {
    path: filename,
  })
  return res
  try {
  } catch (error) {
    console.error(error)
  }
}

export const uploadFileToS3Api = async ({ file }) => {
  try {
    if (!file) throw new Error('No file provided')
    const generateUploadRes = await getUploadUrlApi({ filename: file.name })
    if (!generateUploadRes.data.uploadURL)
      throw new Error('Error generating pre signed URL')
    const uploadRes = await putFileToS3Api({
      uploadURL: generateUploadRes.data.uploadURL,
      file,
    })
    if (!uploadRes.ok) throw new Error('Error uploading file to S3')
    return {
      message: 'File uploaded successfully',
      uploadURL: generateUploadRes.data.uploadURL,
      path: generateUploadRes.data.path,
    }
  } catch (error) {
    console.error(error)
  }
}
Enter fullscreen mode Exit fullscreen mode

调用此uploadFileToS3Api函数,一次性上传要上传到 S3 存储桶的文件。此外,您还可以使用此函数await Promise.all一次上传多个文件。

const uploadFiles = async (files) => {
  const uploadPromises = files.map((file) => uploadFileToS3Api({ file }))
  const uploadResults = await Promise.all(uploadPromises)
  console.log(uploadResults)
}
Enter fullscreen mode Exit fullscreen mode

附加资源

有关 AWS S3 预签名 URL 的更多信息,请点击此处。

结论

好了,你已经成功学会了如何在保证 S3 存储桶私密性的同时,生成用于从 S3 存储桶上传和下载文件的预签名 URL。
希望这篇博客对你有所帮助。如有任何疑问,欢迎在下方评论区留言或在 Twitter 上联系我@thesohailjafri

文章来源:https://dev.to/thesohailjafri/how-to-use-aws-s3-pre-signed-urls-to-upload-and-download-files-4p53