目录
开始
服务端签名直传概述
代码实现
后端实现
前端实现
效果演示
开始
服务端签名直传概述
传统的,我们有两种方式将图片上传到 OSS:
a)前端请求 -> 后端服务器 -> OSS
好处:在服务端上传,更加安全.
坏处:给服务器带来压力.
b)直接写在前端 js 代码中
好处:效率高,不用给服务器带来额外压力.
坏处:危险,用户直接可以看得到 OSS账号密码 信息.
因此最好的方式就是 服务端签名直传:用户直接去服务器请求获取上传签名(账号密码加密生成的防伪签名,一般有过期时间),服务器就返回防伪签名,然后用户就可以拿着签名和要上传的文件,通过表单直接上传到 OSS 中.
此时既不需要服务端承担上传文件到 OSS 中的压力,也保证了 OSS账号密码 信息的安全性.
代码实现
后端实现
a)配置文件
minio:
access-key: root
secret-key: rootroot
endpoint: http://100.105.180.32:9001
bucket: dir1
b)Bean 配置
import org.springframework.context.annotation.Configuration
import io.minio.MinioClient
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
@Configuration
class MinioConfig {
@Value("\${minio.access-key}")
private lateinit var accessKey: String
@Value("\${minio.secret-key}")
private lateinit var secretKey: String
@Value("\${minio.endpoint}")
private lateinit var endpoint: String
@Bean
fun minioClient(): MinioClient = MinioClient.builder()
.endpoint(endpoint) //格式必须是 http://ip:port 注意: 这里使用的 9001 端口,而不是 9000
.credentials(accessKey, secretKey) //用户名 和 密码
.build()
}
c)核心代码
@RestController
@RequestMapping("/third/minio")
class MinioApi(
private val minioClient: MinioClient
) {
@Value("\${minio.endpoint}")
private lateinit var endpoint: String
@Value("\${minio.bucket}")
private lateinit var bucket: String
@GetMapping("/policy")
fun policy(): Map<String, String> {
val objectNamePrefix = "uploads/"
// 设置过期时间为当前时间加1小时
val expiration = ZonedDateTime.now().plusHours(1)
val postPolicy = PostPolicy(bucket, expiration)
postPolicy.run {
//添加条件,确保字段的值以指定前缀开头,前端指定 key 时,也需要加上该前缀,否则 403
addStartsWithCondition("key", objectNamePrefix)
//添加条件,将文件大小范围限制在 1 ~ 10485760 (1 到 10 MB)
addContentLengthRangeCondition(1, 10485760)
}
val result = minioClient.getPresignedPostFormData(postPolicy).apply {
//这里是给前端构造 Minio 请求的
this["url"] = "$endpoint/$bucket"
}
return result
}
}
前端实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minio File Upload</title>
</head>
<body>
<h2>Minio 文件上传</h2>
<input type="file" id="fileInput" />
<button onclick="uploadFile()">上传</button>
<script>
async function uploadFile() {
const fileInput = document.getElementById('fileInput');
if (!fileInput.files.length) {
alert('请选择文件');
return;
}
const file = fileInput.files[0];
const response = await fetch('http://localhost:10010/third/minio/policy');
const formData = await response.json();
const data = new FormData();
for (const [key, value] of Object.entries(formData)) {
data.append(key, value);
}
// 构建对象键(这里一定要和 后端 那里的设定的前缀一致,否则 403)
const objectName = 'uploads/' + file.name;
data.append('key', objectName);
data.append('file', file);
// 上传 URL
const uploadUrl = formData['url'];
const uploadResponse = await fetch(uploadUrl, {
method: 'POST',
body: data,
});
if (uploadResponse.ok) {
alert('上传成功');
} else {
alert('上传失败');
}
}
</script>
</body>
</html>
效果演示
a)点击选择文件,选择 mongo.png
b)点击上传
c)返回 MinIO 控制台,就可以看到文件已经长传成功~