You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

咨询:TypeScript NextJS中AWS S3Client v3预签名URL请求构造方法

在TypeScript Next.js项目中用AWS SDK v3生成S3预签名URL(浏览器无密钥下载)

首先明确核心原则:预签名URL必须在后端(Next.js API路由)生成,不能直接在前端处理——如果把AWS密钥暴露在前端代码中,会导致严重的安全风险。前端只需调用后端API获取预签名URL,即可直接下载文件,无需接触密钥。

1. 安装依赖

需要两个AWS SDK v3包:S3客户端和预签名工具

npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
# 或使用yarn
yarn add @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

2. 配置AWS凭证

在项目根目录的.env.local文件中添加环境变量(绝对不要硬编码密钥):

AWS_ACCESS_KEY_ID=你的AWS访问密钥ID
AWS_SECRET_ACCESS_KEY=你的AWS秘密访问密钥
AWS_REGION=你的S3存储桶所在区域(例如us-east-1)

3. 创建Next.js API路由生成预签名URL

根据你使用的路由方案选择对应代码:

Pages Router 版本(pages/api/generate-presigned-url.ts)

import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import type { NextApiRequest, NextApiResponse } from "next";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== "GET") {
    return res.status(405).json({ message: "仅支持GET请求" });
  }

  const { bucket, key } = req.query;
  if (!bucket || !key) {
    return res.status(400).json({ message: "缺少bucket或key参数" });
  }

  // 初始化S3客户端
  const s3Client = new S3Client({
    region: process.env.AWS_REGION!,
    credentials: {
      accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
    },
  });

  // 构造下载请求(你需要的request对象就是这个Command实例)
  const request = new GetObjectCommand({
    Bucket: bucket as string,
    Key: key as string,
    // 可选:设置响应头强制浏览器下载而非预览
    ResponseContentDisposition: `attachment; filename="${decodeURIComponent(key as string)}"`,
  });

  // 生成预签名URL,有效期设为1小时(3600秒),可自定义
  const presignedUrl = await getSignedUrl(s3Client, request, { expiresIn: 3600 });

  res.status(200).json({ presignedUrl });
}

App Router 版本(app/api/generate-presigned-url/route.ts)

import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { NextResponse } from "next/server";

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const bucket = searchParams.get("bucket");
  const key = searchParams.get("key");

  if (!bucket || !key) {
    return NextResponse.json({ message: "缺少bucket或key参数" }, { status: 400 });
  }

  const s3Client = new S3Client({
    region: process.env.AWS_REGION!,
    credentials: {
      accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
    },
  });

  const requestCommand = new GetObjectCommand({
    Bucket: bucket,
    Key: key,
    ResponseContentDisposition: `attachment; filename="${decodeURIComponent(key)}"`,
  });

  const presignedUrl = await getSignedUrl(s3Client, requestCommand, { expiresIn: 3600 });

  return NextResponse.json({ presignedUrl });
}

4. 前端组件调用API并触发下载

"use client";

import { useState } from "react";

export default function DownloadButton() {
  const [isLoading, setIsLoading] = useState(false);

  const handleDownload = async () => {
    setIsLoading(true);
    try {
      // 替换为你的存储桶名和文件路径
      const bucket = "your-bucket-name";
      const fileKey = "documents/report.pdf";

      // 调用后端API获取预签名URL
      const response = await fetch(`/api/generate-presigned-url?bucket=${bucket}&key=${encodeURIComponent(fileKey)}`);
      const data = await response.json();

      // 创建临时a标签触发下载
      const aTag = document.createElement("a");
      aTag.href = data.presignedUrl;
      aTag.download = fileKey.split("/").pop() || "file";
      document.body.appendChild(aTag);
      aTag.click();
      document.body.removeChild(aTag);
    } catch (error) {
      console.error("下载失败:", error);
      alert("下载失败,请稍后重试");
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <button onClick={handleDownload} disabled={isLoading}>
      {isLoading ? "生成链接中..." : "下载文件"}
    </button>
  );
}

关键细节说明

  • request对象的本质:你提到的request就是AWS SDK v3中对应S3操作的Command实例——下载用GetObjectCommand,上传用PutObjectCommand,构造时传入的参数就是S3 API的请求参数(如Bucket、Key、响应头等)。
  • 安全注意:永远不要在前端代码中写入AWS密钥,必须通过Next.js后端处理凭证,前端仅获取预签名URL。
  • 有效期自定义:getSignedUrl的expiresIn参数可以设置URL的有效时长(单位秒),最大不超过7天(604800秒),具体受限于IAM用户的配置。

内容的提问来源于stack exchange,提问作者Karatekid430

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 05:15:44