MinIO的预签名直传机制

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

image.gif

/*
 * MinIO配置类
 * @Author GuihaoLv
 */
@Configuration
@EnableConfigurationProperties(MinIoProperties.class)
public class MinIoConfiguration {
    @Autowired
    private MinIoProperties properties;
    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(properties.getEndpoint())
                .credentials(properties.getAccessKey(), properties.getSecretKey())
                .build();
    }
}

image.gif

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);
}

image.gif

/**
 生成上传预签名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-_.]", "");
}

image.gif

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 ++] 清除默认baseURL
      headers: {
        '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:调用后端接口生成临时有效的下载URL
    const { data: { data: presignedUrl } } = await getPresignedDownloadUrl(fileName);
    // 2. 创建隐藏链接触发下载
    const link = document.createElement('a');
    link.href = presignedUrl;       // 设置URL
    link.download = fileName;       // 设置下载文件名,需与 MinIO 存储的文件名一致。
    document.body.appendChild(link); // 将链接添加到DOM
    link.click();                    // 模拟点击触发下载
    document.body.removeChild(link); // 移除临时链接
    return true;                     // 表示下载已触发
  } catch (error) {
    throw new Error('下载失败: ' + error.message); // 统一错误处理
  }
};

image.gif

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>
    <input
      v-model="downloadFileName"
      type="text"
      placeholder="请输入文件名(如 test.jpg)"
    />
    <button @click="downloadFile">下载</button>
    <p>{{ downloadStatus }}</p>
  </div>
</template>

image.gif

image.gif 编辑

上传测试结果:

 

image.gif 编辑 image.gif 编辑

下载测试:

image.gif 编辑

相关文章
|
21天前
|
存储 监控 前端开发
大文件上传下载处理方案-断点续传,秒传,分片,合并
本文介绍了大文件上传下载的断点续传技术方案。上传方面,通过前端将大文件分块(如5MB/块),后端使用MinIO存储分块并合并,实现断点续传和秒传功能。下载方面,采用Range请求分片下载,前端合并分片触发下载。技术要点包括:1)前端分块计算MD5;2)后端MinIO存储管理;3)分片校验与合并;4)进度监控和异常处理。该方案解决了大文件传输中断问题,提升用户体验,适用于视频等大文件传输场景,完整代码示例包含前后端实现。
|
21天前
|
人工智能 自然语言处理 Java
大模型应用开发5-SpringAIalibaba实战
本文介绍了SpringAIAlibaba开源项目,该项目基于SpringAI构建,为阿里云通义系列模型提供Java开发实践。主要内容包括: 基础使用:配置模型API、依赖引入、调用示例,支持同步和流式调用; 多种集成方式:对接本地Ollama模型、ChatClient高级API、SSE流式输出; 核心功能实现:提示词模板、结构化输出、持久化内存、文本生成图片/语音; 高级能力:向量数据库、RAG增强检索、工具调用(Tool Calling); MCP协议:标准化工具调用方案,实现服务端工具共享;
|
21天前
|
消息中间件 JavaScript 前端开发
详解事件循环与浏览器渲染机制
摘要:浏览器采用多进程架构,渲染主线程通过事件循环机制处理HTML解析、样式计算、布局等任务。异步机制避免主线程阻塞,任务按优先级在微队列、交互队列等不同队列中调度。JS执行会阻碍渲染,因其与渲染任务共享主线程。渲染流程包含解析、样式计算、布局、分层等阶段,最终由合成线程和GPU完成绘制。transform效率高因其仅影响合成阶段,不涉及主线程。reflow是布局重计算,repaint是绘制指令更新,两者均影响性能。
|
1月前
|
存储 人工智能 关系型数据库
OpenClaw怎么可能没痛点?用RDS插件来释放OpenClaw全部潜力
OpenClaw插件是深度介入Agent生命周期的扩展机制,提供24个钩子,支持自动注入知识、持久化记忆等被动式干预。相比Skill/Tool,插件可主动在关键节点(如对话开始/结束)执行逻辑,适用于RAG增强、云化记忆等高级场景。
882 56
OpenClaw怎么可能没痛点?用RDS插件来释放OpenClaw全部潜力
|
21天前
|
SQL 前端开发 Java
【分层架构】Spring MVC三层架构 / DDD领域驱动四层架构 / 微服务分布式架构(DAO/Mapper/Repository/Service/Controller/Manager)
本文系统解析Java企业级分层架构(Controller/Service/Manager/Repository/DAO/Mapper),阐明各层职责边界、设计原则与典型误区,强调单一职责、依赖倒置、关注点分离等核心思想,助力构建高内聚、低耦合、易维护的可扩展系统。
542 11
|
19天前
|
Python
3个让你爱不释手的Python冷门技巧
3个让你爱不释手的Python冷门技巧
299 146
|
21天前
|
Linux API 网络安全
OpenClaw(Clawdbot)本地+阿里云部署实操:知识库搭建与大模型API对接全流程
在2026年的AI办公实践中,将本地分散的PDF、Markdown、Word等文档转化为可检索、可问答的智能知识库,成为提升工作效率的核心需求。但实际操作中,开发者常面临资料检索效率低、向量库搭建环境依赖复杂、大模型对接流程不清晰等问题。OpenClaw(原Clawdbot)作为轻量级的RAG(检索增强生成)框架,可实现本地文档的快速向量化、检索与问答闭环,同时支持本地多系统(MacOS/Linux/Windows11)与阿里云服务器部署,还能灵活对接阿里云千问系列大模型及免费的Coding Plan API,兼顾数据隐私性与AI问答能力。本文将详细拆解2026年OpenClaw的全平台部署步
2158 13
|
19天前
|
SQL 关系型数据库 MySQL
MySQL 技巧:用好窗口函数,告别复杂子查询
MySQL 技巧:用好窗口函数,告别复杂子查询
|
25天前
|
缓存 Java 数据库
【Spring Boot】Spring Boot 全体系知识结构化拆解(附 Spring Boot 高频面试八股文精简版)
Spring Boot 是 Pivotal 基于 Spring 的“约定大于配置”快速开发框架,简化初始搭建与开发,无缝整合 Spring 全生态,内嵌容器、自动配置、起步依赖开箱即用,是 Java 企业级应用与微服务架构的核心基石。
|
21天前
|
人工智能 NoSQL Java
大模型应用开发2-SpringAI实战
本文介绍了SpringAI框架如何整合大语言模型,并详细讲解了应用开发的关键技术。主要内容包括: 核心功能 支持OpenAI、Ollama等主流平台 封装对话模型、向量计算等功能 提供同步/异步调用方式 关键技术实现 会话记忆管理(内存/Redis) 工具调用(Function Calling) 知识增强(RAG)架构 多模态交互(文本/图像) 典型应用场景 文献阅读助手实现 智能客服系统 文档知识库问答 开发实践 配置向量数据库 处理PDF文档 实现工具调用 兼容阿里云平台 该框架显著简化了大模型应用开发