罗田企业网站建设,河北省最新消息,wordpress二次元美化,哪里制作企业网站#xff08;一#xff09;背景
在当前的文件交付场景中#xff0c;系统部署了 MinIO 作为成品文件的存储与分发平台。通常情况下#xff0c;我们采用 MinIO 官方提供的命令行工具 mc#xff08;MinIO Client#xff09;进行对象文件的拷贝#xff08;下载#xff09;一背景
在当前的文件交付场景中系统部署了 MinIO 作为成品文件的存储与分发平台。通常情况下我们采用 MinIO 官方提供的命令行工具 mcMinIO Client进行对象文件的拷贝下载主要操作流程如下
配置服务别名 使用以下命令设置 MinIO 服务的别名以便后续访问
mc alias set 别名 MinIO服务地址 用户名 密码
执行文件拷贝下载 通过以下命令将指定文件下载到本地
mc cp 别名/桶名/文件路径 ~/download/
然而在实际的文件交付过程中部分用户更倾向于通过点击链接直接下载文件而不希望依赖命令行工具。
二MINIO下载链接生成方式
1.公有桶访问
操作路径将目标桶设置为 public 模式 → 用户可直接通过以下 URL 访问文件
http://MinIO服务地址/桶名/对象路径
此方式不支持设置有效期且存在文件被公开访问的风险安全性较低。
2.预签名下载链接
MinIO 提供生成临时有效的预签名 URL 的方式具体包括
方式一通过 MinIO Web 控制台生成 路径登录 MinIO 控制台 → 选择目标文件 → 点击 “Share” → 获取分享链接
⚠️ 该链接有效期最长为 12 小时不支持更长时间设置。
方式二通过 mc 命令行工具生成 示例命令如下
mc alias set 别名 MinIO服务地址 用户名 密码
mc share download --expire 时间字符串 别名/桶名/文件路径⚠️ 该方式依赖 mc 工具且生成链接的最长有效期为 7 天。 三流式透传代理实现
针对这一需求我们可以基于 Spring Boot 封装一个下载 API通过接口形式提供文件下载服务。该 API 可作为一层代理将 MinIO 中的文件以流的形式透传至客户端实现点击链接即可下载的功能。
1.完整实现代码
先看一下完整代码
POM文件
!-- MinIO Java SDK --dependencygroupIdio.minio/groupIdartifactIdminio/artifactIdversion8.5.9/version/dependencyController层
RestController
RequestMapping(/minio)
RequiredArgsConstructor
public class OpMinioController {GetMapping(/download)public void downloadFile(RequestParam(required false) String endpoint,RequestParam String accessKey,RequestParam String secretKey,RequestParam(required false) String bucket,RequestParam String filePath,HttpServletResponse response) {try {endpoint (endpoint null || endpoint.isEmpty()) ? http://127.0.0.1:9000 : endpoint;bucket (bucket null || bucket.isEmpty()) ? public-bucket : bucket;// 构建 MinIO 客户端MinioClient minioClient MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();// 获取对象元信息获取文件名和 content-typeStatObjectResponse stat minioClient.statObject(StatObjectArgs.builder().bucket(bucket).object(filePath).build());// 提取文件名用于下载提示String fileName Paths.get(filePath).getFileName().toString();// 设置响应头不缓存并作为下载response.setContentType(stat.contentType());response.setHeader(Content-Disposition, attachment; filename\ URLEncoder.encode(fileName, StandardCharsets.UTF_8) \);response.setHeader(Content-Length, String.valueOf(stat.size()));response.setHeader(Cache-Control, no-cache);// 获取对象流并写入响应流流式透传try (InputStream in minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(filePath).build());ServletOutputStream out response.getOutputStream()) {byte[] buffer new byte[8192];int bytesRead;while ((bytesRead in.read(buffer)) ! -1) {out.write(buffer, 0, bytesRead);}out.flush();}} catch (ErrorResponseException e) {// MinIO 返回的业务错误例如找不到文件、访问权限错误response.setStatus(getHttpStatusFromMinioError(e));writeJsonError(response, e.errorResponse().message());} catch (Exception e) {// 其他不可预期的异常response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);writeJsonError(response, 服务内部错误: e.getMessage());}}/*** 输出 JSON 错误响应。** param response HttpServletResponse* param message 错误消息*/private void writeJsonError(HttpServletResponse response, String message) {try {response.setContentType(application/json;charsetUTF-8);response.getWriter().write({\error\:\ message \});} catch (IOException ex) {// 忽略}}/*** 将 MinIO 的错误码映射为 HTTP 状态码** param e MinIO 错误响应异常* return HTTP 状态码*/private int getHttpStatusFromMinioError(ErrorResponseException e) {String code e.errorResponse().code();switch (code) {case NoSuchKey:case NoSuchObject:return HttpServletResponse.SC_NOT_FOUND; // 404 文件不存在case AccessDenied:case InvalidAccessKeyId:case SignatureDoesNotMatch:return HttpServletResponse.SC_UNAUTHORIZED; // 401 认证失败case NoSuchBucket:return HttpServletResponse.SC_NOT_FOUND; // 404 桶不存在default:return HttpServletResponse.SC_BAD_GATEWAY; // 502 其他错误}}
}此接口将各项参数作为路径参数并暴露对外便于调试与改造可以通过浏览器或代码请求这个接口完成下载例如
GET http://localhost:8080/minio/download?endpointhttp://localhost:9000accessKeyminioadminsecretKeyminioadminbuckettest-bucketfilePathexample.pdf2.传统下载和透传代理的区别
传统下载 后端“先下载再转发”占用内存、延迟高。 透传代理 后端“边下载边输出”低延迟、低资源占用表现得像“中间转发服务器”。
对比项传统下载方式透传代理流式转发实现方式后端先将对象完整下载到服务器本地临时文件或内存再输出给客户端后端建立输入流和输出流的桥接边下载边输出无需完整缓存文件处理等待文件全部下载完毕才开始响应文件边读边写立即响应客户端内存/磁盘使用高占用较多内存或临时文件空间低仅使用缓冲区空间如8KB延迟表现高延迟受文件大小影响明显低延迟客户端可更快开始下载失败场景失败可能发生在下载或写出任一阶段前者浪费资源更容易感知异常失败快速反馈给客户端
具体代码实现对比
// 下载并写入响应输出流
try (InputStream in minioClient.getObject(byte[] fileBytes;try (InputStream inputStream minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(filePath).build())) {fileBytes inputStream.readAllBytes(); // 一次性读取全部内容}response.setContentType(stat.contentType());response.setHeader(Content-Disposition, attachment; filename\ URLEncoder.encode(Paths.get(filePath).getFileName().toString(), StandardCharsets.UTF_8) \);response.setContentLength(fileBytes.length); // 需要提前知道长度response.getOutputStream().write(fileBytes);
} catch (Exception e) {throw new RuntimeException(文件下载失败: e.getMessage(), e);
}
// 设置响应头不缓存并作为下载
response.setContentType(stat.contentType());
response.setHeader(Content-Disposition, attachment; filename\ URLEncoder.encode(fileName, StandardCharsets.UTF_8) \);
response.setHeader(Content-Length, String.valueOf(stat.size()));
response.setHeader(Cache-Control, no-cache);// 获取对象流并写入响应流流式透传
try (InputStream in minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(filePath).build());ServletOutputStream out response.getOutputStream()) {byte[] buffer new byte[8192];int bytesRead;while ((bytesRead in.read(buffer)) ! -1) {out.write(buffer, 0, bytesRead);}out.flush();}
} catch (Exception e) {throw new RuntimeException(文件下载失败: e.getMessage(), e);
}