Skip to content

使用 S3 协议从前端上传文件到 OSS

🏷️ OSS

本来是通过后端服务中转来上传文件到 OSS 的,但是为了降低后端服务的带宽流量和服务器资源消耗,决定改为通过前端直接上传到。

通过前端上传时必须要考虑鉴权的问题,公司使用的主要是七牛云,之前的项目中使用过通过 项目凭证 的方式:后端接口给前端提供一次性且带有效期的令牌,前端使用该令牌上传文件。

当前项目有些不一样,使用了 AWS S3 协议来兼容多个品牌的对象存储,所以最好是修改后仍然能兼容其它品牌。

文心一言:

对于 S3 上传,AWS 通常使用 预签名 URL签名策略 来实现。这些签名机制通常涉及 AWS Signature Version 4,这是一种用于对 AWS 请求进行身份验证的机制。

如果你想要生成一个预签名的 S3 URL 用于上传,你应该使用 aws-java-sdk-s3 库中的相关类。例如,你可以使用 AmazonS3 客户端的 generatePresignedUrl 方法来生成一个预签名的 PUT 请求 URL,该 URL 允许用户在指定的时间范围内上传文件到 S3。

根据文心一言的回答,有两种实现方式:预签名 URL签名策略

签名策略 的方案感觉有些类似于七牛云的项目凭证,但是没有找到具体的 API 和示例代码。

七牛云的开发者文档(AWS SDK for Java)中提供了 预签名 URL 方式的示例代码,只是使用的依赖和项目中的不一样。项目中使用的是 aws-java-sdk-s3:1.12.540 ,在 com.amazonaws.services.s3.AmazonS3 类中提供了 generatePresignedUrl 方法来生成预签名的 URL。

示例代码:

java
/**
 * 获取预上传 URL 链接
 *
 * @param objectKey 对象 KEY
 * @param second    授权时间
 */
public String getPreuploadUrl(String objectKey, Integer second) {
    GeneratePresignedUrlRequest generatePresignedUrlRequest =
        new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey)
            .withMethod(HttpMethod.PUT)
            .withExpiration(new Date(System.currentTimeMillis() + 1000L * second));
    URL url = client.generatePresignedUrl(generatePresignedUrlRequest);
    return url.toString();
}

七牛云获取的预签名 URL 结构:

txt
https://test-domain.s3.cn-south-1.qiniucs.com/test/2024/05/30/cab0fe332cd843289142807feb2512e2.svg
?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Date=20240530T092929Z
&X-Amz-SignedHeaders=host
&X-Amz-Expires=1199
&X-Amz-Credential=azq7eaI1pgl4N78TRBmV4zdV5WH-_U9QI52jn12d%2F20240530%2F%2Fs3%2Faws4_request
&X-Amz-Signature=0db13fda7b4982a4c2704b79e84db4215bcdb1457ab0013da1c9954c76bad35d

最后前端通过发送 PUT 请求到该地址即可将文件上传到服务器。

bash
curl -X PUT --upload-file "<path/to/file>" "<presigned url>"