后端
因为RustFs兼容s3,所以可以直接使用s3的方式来实现上传地址的签名
具体可以参考官网:https://docs.rustfs.com.cn/developer/sdk/java.html#五、java-高级功能示例
添加依赖
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.25.27</version>
</dependency>
注入s3配置
S3Config.java
package com.example.java_web_test1.s3.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import java.net.URI;
@Configuration
public class S3Config {
@Value("${endpoint:http://127.0.0.1:9000}")
private String endpoint;
@Value("${accessKeyId:rustfsadmin}")
private String accessKeyId;
@Value("${secretAccessKey:rustfsadmin}")
private String secretAccessKey;
@Bean
public S3Client buildS3Client() {
return S3Client.builder()
.endpointOverride(URI.create(endpoint)) // RustFS 地址
.region(Region.US_EAST_1) // 可写死,RustFS 不校验 region
.credentialsProvider(
StaticCredentialsProvider.create(
AwsBasicCredentials.create(accessKeyId, secretAccessKey)
)
)
.forcePathStyle(true) // 关键配置!RustFS 需启用 Path-Style
.build();
}
@Bean
public S3Presigner buildS3Presigner() {
return S3Presigner.builder()
.endpointOverride(URI.create(endpoint))
.region(Region.US_EAST_1)
.credentialsProvider(
StaticCredentialsProvider.create(
AwsBasicCredentials.create(accessKeyId, secretAccessKey)
)
)
.build();
}
}
S3Controller.java
package com.example.java_web_test1.s3.controller;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
import java.time.Duration;
import java.util.UUID;
@RestController
@RequestMapping("/s3")
public class S3Controller {
@Resource
private S3Presigner presigner;
@GetMapping("/presignedUrl")
public String getPresignedUrl(){
String key = UUID.randomUUID().toString();
PutObjectRequest putRequest = PutObjectRequest.builder()
.bucket("test1")
.key(key)
.build();
PresignedPutObjectRequest presignedPut = presigner.presignPutObject(
PutObjectPresignRequest.builder()
.putObjectRequest(putRequest)
.signatureDuration(Duration.ofMinutes(10))
.build()
);
return presignedPut.url().toString();
}
}
前端
request.ts
import axios from 'axios'
const instance = axios.create({
baseURL:"http://localhost:8080"
});
export default instance
fileUtils.ts
import request from "./request.ts";
import type {AxiosProgressEvent} from "axios";
/**
* 获取一个经过签名的文件上传地址
*/
export function generatePresignedUrlApi() {
return request({
url: '/s3/presignedUrl',
method: 'get',
})
}
/**
* 文件上传
* @param file 文件
* @param progressEvent 上传进度回调
*/
export function fileUploadApi(file: Blob, progressEvent: (progressEvent: AxiosProgressEvent) => void) {
return new Promise((resolve, reject) => {
generatePresignedUrlApi().then(res => {
const url = res.data as string
request({
url: url,
method: 'put',
headers: {
"Content-Type": "application/octet-stream"
},
data: file,
onUploadProgress: progressEvent,
}).then(() => {
// 返回去掉签名的url地址,通常为公共读的情况下,改地址可直接访问到文件
resolve(url.split("?")[0])
}).catch(reject)
})
})
}
FileUpload.vue
<script setup lang="ts">
import {ref} from "vue";
import {fileUploadApi} from '../utils/fileUtils.ts'
import {Plus} from "@element-plus/icons";
import type {UploadFile, UploadProgressEvent, UploadRequestOptions} from 'element-plus';
const fileList = ref([])
/**
* 自定义文件上传处理
* @param option
*/
const handleHttpReq = (option: UploadRequestOptions) => {
fileUploadApi(option.file, e => {
// 设置上传进度
option.onProgress({
percent: e.progress as number * 100
} as UploadProgressEvent)
}).then(url => {
// 设置上传进度
option.onProgress({
percent: 100
} as UploadProgressEvent)
// 调用上传成功回调
option.onSuccess(url)
})
}
/**
* 文件上传成功处理
* 把自定义上传返回的url设置到文件的url字段上
* @param response 文件上传成功的响应
* @param uploadFile 上传的文件对象
*/
const handleSuccess = (response: string, uploadFile: UploadFile) => {
uploadFile.url = response
}
</script>
<template>
<div>
<h2>文件上传</h2>
<el-upload
v-model:file-list="fileList"
list-type="picture-card"
:http-request="handleHttpReq"
:on-success="handleSuccess"
multiple
>
<el-icon>
<Plus/>
</el-icon>
</el-upload>
</div>
</template>
<style scoped>
</style>