MinIO的预签名直传机制

我们传统使用MinIo做OSS对象存储的应用方式往往都是在后端配置与MinIO的连接和文件上传下载的相关接口,然后我们在前端调用这些接口完成文件的上传下载机制,但是,当并发量过大,频繁访问会对后端的并发往往会对服务器造成极大的压力,大文件传输场景下,服务器被迫承担数据中转的角色,既消耗大量带宽资源,又形成单点性能瓶颈。这时,我们引入了MinIO的一种预签名机制。

预签名机制:在后端对文件的上传和下载操作生成一个URL,前端针对不同的文件操作形式请求会获取到对应的URL,这个URL可以理解为一个临时的通行证,有了这个URL后,前端可以直接向MinIO的服务端发上传和下载的相应请求,与MinIO直连操作,大大减缓了对后端服务器的压力

1.后端配置

1.1 引入Maven依赖并配置MinIO

<!--minio-->
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId>
</dependency>
/** MinIO配置类* @Author GuihaoLv*/
@Configuration
@EnableConfigurationProperties(MinIoProperties.class)
public class MinIoConfiguration {@Autowiredprivate MinIoProperties properties;@Beanpublic MinioClient minioClient() {return MinioClient.builder().endpoint(properties.getEndpoint()).credentials(properties.getAccessKey(), properties.getSecretKey()).build();}}

1.2 生成预签名接口封装:

/**要改成使用预签名URL,让前端直接与MinIO交互,减轻服务器负担。* 生成上传预签名URL(PUT)* @param fileName* @return*/
@GetMapping("/presigned-upload-url")
@ApiOperation("获取上传预签名URL")
public Result<String> generateUploadUrl(@RequestParam("fileName") String fileName) {System.out.println("测试"+fileName);String url = commonFileService.generatePresignedUploadUrl(fileName);System.out.println("结构"+url);return Result.success(url);
}/**要改成使用预签名URL,让前端直接与MinIO交互,减轻服务器负担。* 生成下载预签名URL(GET)* @param fileName* @return*/
@GetMapping("/presigned-download-url")
@ApiOperation("获取下载预签名URL")
public Result<String> generateDownloadUrl(@RequestParam("fileName") String fileName) {String url = commonFileService.generatePresignedDownloadUrl(fileName);return Result.success(url);
}
/**生成上传预签名URL(PUT)* @param fileName* @return*/
public String generatePresignedUploadUrl(String fileName) {try {// 安全处理文件名(防止路径遍历)String safeFileName = sanitizeFileName(fileName);// 生成预签名URL(PUT方法)return client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.PUT).bucket(properties.getBucketName()).object(safeFileName).expiry(15, TimeUnit.MINUTES) // 15分钟有效.build());} catch (Exception e) {throw new RuntimeException("生成预签名URL失败", e);}
}/*** 生成下载预签名URL(GET)* @param fileName* @return*/
public String generatePresignedDownloadUrl(String fileName) {try {String safeFileName = sanitizeFileName(fileName);return client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(properties.getBucketName()).object(safeFileName).expiry(1, TimeUnit.HOURS) // 1小时有效.build());} catch (Exception e) {throw new RuntimeException("生成预签名URL失败", e);}
}// 文件名安全处理
private String sanitizeFileName(String fileName) {// 过滤非法字符,防止路径遍历return fileName.replaceAll("[^a-zA-Z0-9-_.]", "");
}

1.3 前端封装获取预签名和直连MinIO做上传下载的请求

// 获取上传预签名URL
export const getPresignedUploadUrl = (fileName) => {return httpInstance({url: '/web/commonFile/presigned-upload-url',method: 'GET',params: { fileName },});
};// 获取下载预签名URL
export const getPresignedDownloadUrl = (fileName) => {return httpInstance({url: '/web/commonFile/presigned-download-url',method: 'GET',params: { fileName },});
};// 单个文件直传MinIO,上传文件
export const uploadViaPresignedUrl = async (file: File) => {try {// 步骤1: 获取未编码的原始文件名(需与后端生成的签名匹配)const rawFileName = file.name;// 步骤2: 调用后端接口获取预签名URL(必须传递原始文件名)const res=await getPresignedUploadUrl(rawFileName);const presignedUrl=res.data;// 调试输出:验证URL格式console.log('[DEBUG] 预签名URL:', presignedUrl); // 应输出类似 http://47.99.49.193:9000/...// 步骤3: 直接向MinIO发送PUT请求(绕过代理)const response = await axios.put(presignedUrl, file, {// 关键配置:禁用代理和默认请求头baseURL: '', // [!code ++] 清除默认baseURLheaders: {'Content-Type': 'application/octet-stream' // MinIO通用类型}});return response.data;} catch (error) {throw new Error(`上传失败: ${(error).response?.data || error.message}`);}
};// 使用预签名URL直连MinIO下载文件
export const downloadViaPresignedUrl = async (fileName) => {try {// 1. 获取预签名URL:调用后端接口生成临时有效的下载URLconst { data: { data: presignedUrl } } = await getPresignedDownloadUrl(fileName);// 2. 创建隐藏链接触发下载const link = document.createElement('a');link.href = presignedUrl;       // 设置URLlink.download = fileName;       // 设置下载文件名,需与 MinIO 存储的文件名一致。document.body.appendChild(link); // 将链接添加到DOMlink.click();                    // 模拟点击触发下载document.body.removeChild(link); // 移除临时链接return true;                     // 表示下载已触发} catch (error) {throw new Error('下载失败: ' + error.message); // 统一错误处理}
};

1.4:写一个前端页面测试前端直连MinIO的功能实现

<script setup lang="ts">
import { ref } from 'vue';
import {uploadViaPresignedUrl,downloadViaPresignedUrl
} from '@/api/file';// 定义响应式变量
const selectedFile = ref<File | null>(null); // 存储用户选择的文件
const downloadFileName = ref<string>('');    // 下载时输入的文件名
const uploadStatus = ref<string>('');        // 上传状态提示
const downloadStatus = ref<string>('');      // 下载状态提示// 处理文件选择事件
const handleFileChange = (event: Event) => {const target = event.target as HTMLInputElement;if (target.files && target.files.length > 0) {selectedFile.value = target.files[0];uploadStatus.value = ''; // 重置上传状态}
};// 上传文件到MinIO
const uploadFile = async () => {uploadStatus.value = '上传中...';await uploadViaPresignedUrl(selectedFile.value);uploadStatus.value = '上传成功!';selectedFile.value = null; // 清空文件选择
}// 下载文件从MinIO
const downloadFile = async () => {downloadStatus.value = '正在触发下载...';const success = await downloadViaPresignedUrl(downloadFileName.value);if (success) {downloadStatus.value = '下载已触发!';}};
</script><template><div class="container"><!-- 上传文件部分 --><h2>测试MinIO文件上传</h2><input type="file" @change="handleFileChange" /><button @click="uploadFile" :disabled="!selectedFile">上传</button><p>{{ uploadStatus }}</p><!-- 下载文件部分 --><h2>测试MinIO文件下载</h2><inputv-model="downloadFileName"type="text"placeholder="请输入文件名(如 test.jpg)"/><button @click="downloadFile">下载</button><p>{{ downloadStatus }}</p></div>
</template>

上传测试结果:
 

下载测试:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/34827.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【NLP 38、实践 ⑩ NER 命名实体识别任务 Bert 实现】

去做具体的事&#xff0c;然后稳稳托举自己 —— 25.3.17 数据文件&#xff1a; 通过网盘分享的文件&#xff1a;Ner命名实体识别任务 链接: https://pan.baidu.com/s/1fUiin2um4PCS5i91V9dJFA?pwdyc6u 提取码: yc6u --来自百度网盘超级会员v3的分享 一、配置文件 config.py …

Windows下安装MongoDB 8

在Windows下安装MongoDB&#xff0c;首先需要确定自己的Windows系统版本以及MongoDB社区版所能支持的系统版本。这里使用的是Windows 10操作系统和MongoDB 8.0.4版本。由于MongoDB 6版本之后&#xff0c;不再默认安装Mongo Shell&#xff0c;所以本节分为两部分&#xff1a;安装…

【Node.js入门笔记4---fs 目录操作】

Node.js入门笔记4 Node.js---fs 目录操作一、目录操作1.fs.mkdir()&#xff1a;创建目录。异步&#xff0c;非阻塞。创建单个目录创建多个目录创建目前之前需要确认是否存在&#xff1a; 2. fs.mkdirSync()&#xff1a;用于创建一个新的目录。异步&#xff0c;非阻塞。3.fs.rmd…

DeepSeek-R1思路训练多模态大模型-Vision-R1开源及实现方法思路

刚开始琢磨使用DeepSeek-R1风格训练多模态R1模型&#xff0c;就看到这个工作&#xff0c;本文一起看看&#xff0c;供参考。 先提出问题&#xff0c;仅靠 RL 是否足以激励 MLLM 的推理能力&#xff1f; 结论&#xff1a;不能&#xff0c;因为如果 RL 能有效激励推理能力&#…

Python学习第十八天

Django模型 定义&#xff1a;模型是 Django 中用于定义数据库结构的 Python 类。每个模型类对应数据库中的一张表&#xff0c;类的属性对应表的字段。 作用&#xff1a;通过模型&#xff0c;Django 可以将 Python 代码与数据库表结构关联起来&#xff0c;开发者无需直接编写 S…

总结 HTTP 协议的基本格式, 相关知识以及抓包工具fiddler的使用

目录 1 HTTP是什么 2 HTTP协议格式 3 HTTP请求(Request) 3.1 认识URL 3.2 方法 3.3 认识请求"报头"(header) 3.3.1 Host 3.3.2 Content-Length 3.3.3 Content-Type 3.3.4 User-Agent (简称UA) 3.3.5 Referer 3.3.6 Cookie和Session 4 HTTP响应详解 4.…

【sql靶场】第15、16关-post提交盲注保姆级教程

目录 【sql靶场】第15、16关-post提交盲注保姆级教程 1.知识回顾 ‌GET请求‌ ‌POST请求‌ or与and 2.第十五关 1.布尔盲注的手动注入 1.判断 2.数据库名长度 3.数据库名字符 4.表名数 5.表名长度 6.表名符 7.字段数 8.字段长度 9.字段符 2.布尔盲注的脚本注入…

【C++】 —— 笔试刷题day_6

刷题day_6&#xff0c;继续加油哇&#xff01; 今天这三道题全是高精度算法 一、大数加法 题目链接&#xff1a;大数加法 题目解析与解题思路 OK&#xff0c;这道题题目描述很简单&#xff0c;就是给我们两个字符串形式的数字&#xff0c;让我们计算这两个数字的和 看题目我…

redis终章

1. 缓存(cache) Redis最主要的用途&#xff0c;三个方面1.存储数据&#xff08;内存数据库&#xff09;&#xff1b;2.缓存[redis最常用的场景]&#xff1b;3.消息队列。 缓存(cache)是计算机中的⼀个经典的概念.核⼼思路就是把⼀些常⽤的数据放到触⼿可及(访问速度更快)的地⽅…

Matlab 多输入系统极点配置

1、内容简介 略 Matlab 172-多输入系统极点配置 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 clc close all clear A [-6.5727 1.1902 0 -53.4085;1.1902 -6.5727 0 -53.4085;0.5294 0.5294 0 17.7502;0 0 1 0]; B [1.3797 -0.2498;-0.2498 1.3797;-0.1111 -0.1…

国产编辑器EverEdit - 脚本(解锁文本编辑的无限可能)

1 脚本 1.1 应用场景 脚本是一种功能扩展代码&#xff0c;用于提供一些编辑器通用功能提供不了的功能&#xff0c;帮助用户在特定工作场景下提高工作效率&#xff0c;几乎所有主流的编辑器、IDE都支持脚本。   EverEdit的脚本支持js(语法与javascript类似)、VBScript两种编程…

Flutter 小技巧之通过 MediaQuery 优化 App 性能

许久没更新小技巧系列&#xff0c;温故知新&#xff0c;在两年半前的《 MediaQuery 和 build 优化你不知道的秘密》 我们聊过了在 Flutter 内 MediaQuery 对应 rebuild 机制&#xff0c;由于 MediaQuery 在 MaterialApp 内&#xff0c;并且还是一个 InheritedWidget &#xff0…

AI-医学影像分割方法与流程

AI医学影像分割方法与流程–基于低场磁共振影像的病灶识别 – 作者:coder_fang AI框架&#xff1a;PaddleSeg 数据准备&#xff0c;使用MedicalLabelMe进行dcm文件标注&#xff0c;产生同名.json文件。 编写程序生成训练集图片&#xff0c;包括掩码图。 代码如下: def doC…

【蓝桥杯每日一题】3.16

&#x1f3dd;️专栏&#xff1a; 【蓝桥杯备篇】 &#x1f305;主页&#xff1a; f狐o狸x 目录 3.9 高精度算法 一、高精度加法 题目链接&#xff1a; 题目描述&#xff1a; 解题思路&#xff1a; 解题代码&#xff1a; 二、高精度减法 题目链接&#xff1a; 题目描述&…

人工智能组第一次培训——deepseek本地部署和知识库的建立

deepseek本地部署的用处 减少对网络依赖性&#xff1a; 在断网环境下&#xff0c;依然可以使用预先下载的AI模型进行处理&#xff0c;避免因网络不稳定而无法完成任务。 提高响应速度&#xff1a; 数据和模型已经在本地设备上准备好&#xff0c;可以直接调用&#xff0c;不…

windows协议不再续签,华为再无windows可用,将于四月发布鸿蒙PC

大家好&#xff0c;我是国货系创始人张云泽&#xff0c;最近不少小伙伴在后台问&#xff1a;“听说Windows协议要到期了&#xff1f;我的电脑会不会变砖&#xff1f;”还有人说&#xff1a;“华为笔记本以后用不了Windows了&#xff1f;鸿蒙系统能用吗&#xff1f;”今天咱们就…

数据结构-----初始数据结构、及GDB调试

一、数据结构核心概念 相互之间存在一种或多种特定关系的数据元素的集合。 1. 数据结构定义 // 嵌入式场景示例&#xff1a;传感器网络节点结构 struct SensorNode {uint16_t node_id; // 2字节float temperature; // 4字节uint32_t timestamp; // 4字节struct Se…

HOT100(1)

目前想到的办法是暴力枚举&#xff0c;有什么更好的办法请多指教。。。。代码如下&#xff1a; 让数组第一个元素和后面的元素相加判断是否相等&#xff0c;让数组第二个元素与后面的元素相加判断是否相等&#xff0c;以此类推 /** * Note: The returned array must be mallo…

QuickAPI 和 DBAPI 谁更香?SQL生成API工具的硬核对比(一)

最近低代码开发火得不行&#xff0c;尤其是能把数据库秒变API的工具&#xff0c;简直是开发者的救星。今天咱就聊聊两款国内玩家&#xff1a;QuickAPI&#xff08;麦聪软件搞出来的低代码神器&#xff09;和 DBAPI&#xff08;开源社区的硬核作品&#xff09;。这两货都能靠SQL…

MySQL单表查询大全【SELECT】

山再高&#xff0c;往上攀&#xff0c;总能登顶&#xff1b;路再长&#xff0c;走下去&#xff0c;定能到达。 Mysql中Select 的用法 ------前言------【SELECT】0.【准备工作】0.1 创建一个库0.2 库中创建表0.3 表中加入一些数据 1.【查询全部】2.【查询指定列】2.1查询指定列…