SpringBoot + MinIO + kkFile 实现文件预览,这样操作更安全!

科技   2024-11-15 15:43   安徽  

来源:juejin.cn/post/7365764222838243366

👉 欢迎加入小哈的星球,你将获得: 专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 点击查看项目介绍
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2期已完结,演示链接:http://116.62.199.48/;

截止目前,累计输出 68w+ 字,讲解图 2776+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,解锁全部项目,已有2400+小伙伴加入

前言

之前写过一篇文章,介绍了如何使用kkfile预览文件,但是文章中介绍的方案,会暴露minio的地址,实际的预览地址如下:

minio生成的文件预览地址:http://kkfile-server/onlinePreview?url=base64UrlEncode

但是大多数情况下,minio服务的地址是不允许暴露的,所有我们对其进行优化,依然使用kkfile预览文件,但是我们使用文件流的方式,并且在下载接口上校验用户认证的有效性,在保证不暴露minio地址的前提下,还加入了token认证,提高了安全性,话不多说,直接上代码。

一、文件上传

上传服务

public void uploadFile(MultipartFile file) throws Exception {
    String fileName = System.currentTimeMillis() + "-" + file.getOriginalFilename();
    PutObjectArgs args = PutObjectArgs.builder().bucket(minioConfig.getBucketName()).object(fileName).stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
    client.putObject(args);
}

封装接口

@PostMapping("upload")
public RestResult<SysFile> upload(MultipartFile file) {
    try {
        sysFileService.uploadFile(file);
    } catch (Exception e) {
        log.error("上传文件失败", e);
        return RestResult.fail(e.getMessage());
    }
}

二、文件下载

下载服务

public void download(String filename, HttpServletResponse response) throws ServiceException {
    try {
        InputStream inputStream = client.getObject(GetObjectArgs.builder().bucket(minioConfig.getBucketName()).object(filename).build());


        // 设置响应头信息,告诉前端浏览器下载文件
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-Disposition""attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));

        // 获取输出流进行写入数据
        OutputStream outputStream = response.getOutputStream();
        // 将输入流复制到输出流
        byte[] buffer = new byte[4096];
        int bytesRead = -1;

        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
        // 关闭流资源
        inputStream.close();
        outputStream.close();
    } catch (Exception e) {
        log.error("文件下载失败:" + e.getMessage());
        throw new ServiceException("文件下载失败");
    }
}

封装接口

@ApiOperation("文件下载")
@GetMapping("/download/{token}/{filename}")
public void getDownload(@PathVariable("token") String token, @PathVariable("filename") String filename, HttpServletResponse response) {
    tokenUtils.validateToken(token);
    sysFileService.download(filename, response);
}

上面的接口有两个地方需要注意

  • @GetMapping("/download/{token}/{filename}")中filename参数必须放在最后
  • tokenUtils.validateToken(token); 该接口要在拦截器中放行,验证token在代码逻辑中,这里根据项目中实际场景去实现。该地址为kkfile请求获取文件流的地址,所以需要放开鉴权

三、文件预览地址获取

文件预览地址生成服务(该服务只是获取token并拼接到文件下载地址中,不对token做验证,因为该服务的接口在请求进入前要做校验)

public String getPreviewUrl(String filename) throws UnsupportedEncodingException {
    ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = sra.getRequest();
    if (request ==null || StringUtils.isBlank(request.getHeader(TokenConstants.AUTHENTICATION))) {
        throw new ServiceException("未获取到有效token");
    }
    String previewUrl = filePreviewUrl + FileUploadUtils.base64UrlEncode(fileDownloadUrl + "/" + token + "/" + filename);
    return previewUrl + "&fullfilename=" + URLEncoder.encode(filename, "UTF-8");
}

FileUploadUtils中的base64UrlEncode方法

public static String base64UrlEncode(String url) throws UnsupportedEncodingException {
    String base64Url = Base64.getEncoder().encodeToString(url.getBytes(StandardCharsets.UTF_8));
    return URLEncoder.encode(base64Url, "UTF-8");
}

封装接口,获取文件预览地址

@GetMapping("/getPreviewUrl")
public RestResult<String> getPreviewUrl(String filename) throws UnsupportedEncodingException {
    return RestResult.ok(sysFileService.getPreviewUrl(filename));
}

测试

假设

  • 文件服务地址为:http://file-server
  • kkfile服务地址为:http://kkfile-server
  • 文件名称为:xxxx.docx

最后生成的文件预览地址为:

http://kkfile-server/onlinePreview?url=aHR0cDovLzE3Mi4xNi41MC4y....&fullfilename=xxxx.docx

其中aHR0cDovLzE3Mi4xNi41MC4y....为:

FileUploadUtils.base64UrlEncode("http://file-server" + "/" + token + "/" + filename);

截图为证

图片

👉 欢迎加入小哈的星球,你将获得: 专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 点击查看项目介绍
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2期已完结,演示链接:http://116.62.199.48/;

截止目前,累计输出 68w+ 字,讲解图 2776+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,解锁全部项目,已有2400+小伙伴加入


1. 我的私密学习小圈子,从0到1手撸企业实战项目!

2. SpringBoot 官方强烈推荐,连接池,太快了!

3. 发现 XSS 漏洞?别急,用这招 SpringBoot 技巧轻松搞定!

4. 这款轻量级 Java 表达式引擎,真不错!

最近面试BAT,整理一份面试资料Java面试BATJ通关手册,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。

获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。

PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下在看,加个星标,这样每次新文章推送才会第一时间出现在你的订阅列表里。

“在看”支持小哈呀,谢谢啦

小哈学Java
码龄9年,前某厂中台研发。专注于Java领域干货分享,不限于BAT面试, 算法,数据库,Spring Boot, 微服务,高并发, JVM, Docker容器,ELK相关知识,期待与您一同进步。
 最新文章