时效性文件链接实现思路

2023-12-04

1.写在前面

之前在某个项目中,用户上传的文件(头像、视频、文档等等)是通过静态路径来访问的,这导致一旦该文件的路径暴露,用户可以在不登录的情况下,直接访问服务器的文件资源。客户因此提出,文件的路径必须要具有时效性(类似对象存储的文件链接,超过一定时间就无法访问)。
我希望最终可以像对象存储一样,文件链接可以设定访问时间,超期后直接报错。比较常见的方法可以通过缓存来实现。思路如下:
1.后端在接收到文件的访问请求时,生成一个唯一的文件ID,将它和指定的前缀拼接为URL返还给前端,并同时将此ID作为key,文件信息作为value存入缓存信息。
2.前端通过返回的文件链接访问后端,后端对链接中的ID进行截取,前往缓存中查询,如果存在则以流的的形式返回文件,否则直接返回错误信息。
时序图如下:

2.时序图

时效性链接时序图.png

3.关键技术点

3.1时效性

对于时效性,我们可以通过缓存来实现。通过给缓存添加时间限制,到期就移除,从而达到URL的时效性。这里我们通过简单的缓存工具类来实现(也可用redis,数据库理论上也可以,通过存文件的存入时间,每次文件请求的时候判断一下是否超期,思路上是可以的,但是考虑访问速度并不推荐)。
以下是缓存对象:


  

java

复制代码

@AllArgsConstructor public class CacheEntry<V> { //存储数据 private final V value; //过期时间 private final long expirationTimeMillis; /** * 是否过期 * @return 布尔值 */ public boolean isExpired() { return System.currentTimeMillis() > expirationTimeMillis; } public V getValue() { return value; } }

再写个简单的工具类来操作:


  

java

复制代码

@Component public class CacheManagerUtil<K,V> { private final Map<K, CacheEntry<V>> cacheMap; private final ScheduledExecutorService scheduler; /** * 默认过期时间 3小时 */ public static long TTL=1000*60*60*3L; public CacheManagerUtil() { cacheMap = new ConcurrentHashMap<>(); scheduler = Executors.newScheduledThreadPool(1); } /** * 存值 * @param key 键 * @param value 值 * @param expirationTimeMillis 过期时间 */ public void put(K key, V value, long expirationTimeMillis) { expirationTimeMillis += System.currentTimeMillis(); CacheEntry<V> entry = new CacheEntry<>(value, expirationTimeMillis); cacheMap.put(key, entry); // 定时任务,在过期时间后自动销毁缓存条目 scheduler.schedule(() -> cacheMap.remove(key), expirationTimeMillis, TimeUnit.MILLISECONDS); } /** * 根据键取值 * @param key 键 * @return 值 */ public V get(K key){ CacheEntry<V> entry = cacheMap.get(key); if (entry != null && !entry.isExpired()) { return entry.getValue(); } return null; } /** * 根据建删除对应的键值对 * @param key 键 */ public void remove(K key) { cacheMap.remove(key); } /** * 获取缓存键列表 * @return 缓存键列表 */ public List<K> getKeys(){ return new ArrayList<>(cacheMap.keySet()); } }

3.2同一个接口返回文件资源和错误信息

首先我们要知道,浏览器在访问某个链接的时候,会根据服务器返回的Response的中的Content-type来决定如何执行响应。如果服务器未指定任何Content-type,浏览器有内置的处理能力来对常见的链接(图片、PDF)进行处理,常见的比如说使用浏览器去打开图片,打开控制台,浏览器会有一个默认的网页将文件链接放在里面。
因此,我们可以通过同一个Mapping,实现不同的Response的返回,从而达到既能返回文件,又可以返回错误信息。需要的Response格式如下:

1.流式文件下载


  

java

复制代码

response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileCacheVO.getFileName()); response.setHeader("pragma", "no-cache"); response.setHeader("cache-control", "no-cache"); response.setHeader("expires", "0");

2.json格式错误返回


  

vbscript

复制代码

response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8");

这里可以使用策略模式进行改写,首先我们构建一个简单的处理响应的接口:


  

java

复制代码

public interface ResponseStrategy { /** * 处理响应 * @param response 响应 * @param fileCacheVO 文件缓存VO * @return 操作结果 */ void handleResponse(HttpServletResponse response, FileCacheVO fileCacheVO); }

A. 下载文件策略处理类


  

java

复制代码

@Slf4j @Component public class DownLoadFileStrategy implements ResponseStrategy { /** * 最大字节大小 */ private static final int MAX_BYTE_SIZE = 4096; @Override public void handleResponse(HttpServletResponse response, FileCacheVO fileCacheVO) { //设置响应头 response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileCacheVO.getFileName()); response.setHeader("pragma", "no-cache"); response.setHeader("cache-control", "no-cache"); response.setHeader("expires", "0"); //以流的形式返回文件 Path filePath=Paths.get(fileCacheVO.getFilePath()); try { Resource resource = new UrlResource(filePath.toUri()); InputStream inputStream = Objects.requireNonNull(resource).getInputStream(); var outputStream = response.getOutputStream(); byte[] buffer = new byte[MAX_BYTE_SIZE]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } outputStream.close(); } catch (MalformedURLException e) { log.error("文件下载失败{}", e.getMessage()); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } catch (IOException e) { log.error("文件IO异常{}", e.getMessage()); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } }

B.无文件策略类


  

java

复制代码

@Slf4j public class NoFileStrategy implements ResponseStrategy { @Override public void handleResponse(HttpServletResponse response, FileCacheVO fileCacheVO) { response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); String errorJson=new ResponseResult<String>().setHttpResultEnum(HttpResultEnum.SERVER_ERROR).setMsg("文件不存在").toJsonString().toString(); try { response.getWriter().write(errorJson); } catch (IOException e) { log.error("文件下载失败{}",e.getMessage()); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } }

4.流程及源码分析

4.1文件上传接口

文件上传我们可以分为三步:文件接收并存储、数据库记录插入和存入缓存。具体代码如下:


  

java

复制代码

/** * 上传文件 * * @param uploadFile 上传文件 * @return 返回临时链接 */ @PostMapping("/uploadFile") @Transactional(rollbackFor = Exception.class) public ResponseResult<FileVO> uploadFile(@Validated @NotNull(message = "上传文件不能为空") MultipartFile uploadFile) { ResponseResult<FileVO> responseResult = new ResponseResult<>(); //文件上传 FileUploadResult fileUploadResult = fileService.uploadFile(uploadFile); //加入缓存 FileCacheVO fileCacheVO = new FileCacheVO(fileUploadResult); cacheManagerUtil.put(fileUploadResult.getFileId(), fileCacheVO, CacheManagerUtil.TTL); return responseResult.setData(new FileVO(fileUploadResult)); }

这里我们通过 fileService 同时进行文件的存储和数据库插入,然后通过缓存工具类 cacheManagerUtil 存储,使用Apifox进行接口测试,结果如下:


其中返回的文件链接为:
<http://127.0.0.1:8080/timelyFileLink/file/22bb8cda-99d8-4b0b-a6aa-9e3d516b5a5f>
URL中的 22bb8cda-99d8-4b0b-a6aa-9e3d516b5a5f >https://link.juejin.cn?target=http%3A%2F%2F127.0.0.1%3A8080%2FtimelyFileLink%2Ffile%2F22bb8cda-99d8-4b0b-a6aa-9e3d516b5a5f 就是我们缓存到服务器上的key

4.2链接校验

这里我们分为两步:缓存校验和返回不同的Response。代码如下:


  

java

复制代码

/** * 文件下载 * * @param uuid 文件唯一ID * @param response 响应 */ @GetMapping("/file/{uuid}") public void file(@Validated @PathVariable @NotBlank(message = "文件ID不能为空") String uuid, HttpServletResponse response) { //校验 FileCacheVO fileCacheVO = cacheManagerUtil.get(uuid); ResponseStrategy strategy = fileCacheVO == null ? new NoFileStrategy() : new DownLoadFileStrategy(); strategy.handleResponse(response, fileCacheVO); }

我们先使用缓存工具类进行校验,如果文件是超期或者不存在的,浏览器会直接返回错误的json信息:

image.png


如果文件是存在,浏览器输入这个链接会直接下载:

image.png


如果html中有地方调用了这个图片,那么图片会自动反显,这里我们写一个简单的html测试代码:


  

html

复制代码

<html> <img src="http://127.0.0.1:8080/timelyFileLink/file/22bb8cda-99d8-4b0b-a6aa-9e3d516b5a5f"> </html>

页面如下:

image.png


可以看到对于流式的接口,浏览器会自动反显图片。

5.总结

整个Demo都是基于缓存操作来实现链接的时效性,对于需要展示文件链接的页面,可以通过这种时效性链接来实现访问文件的安全性。但由于是操作缓存,实际我们在使用的时候需要考虑用户数量、接口频率、缓存大小等等问题,如果是正式项目,我其实更建议使用 redis ,方便进行缓存的管理以及问题的排查。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

时效性文件链接实现思路 的相关文章

随机推荐

  • 手机没Root?你照样可以渗透路由器

    和Metasploit差不多 RouterSploit是一个强大的漏洞利用框架 用于快速识别和利用路由器中的普通漏洞 它还有个亮点 就是可以在绝大多数安卓设备上运行 如果你想在电脑上运行 可以阅读这篇文章 而这篇教程则会告诉你如何轻松地在未
  • 【Linux】 服务器优化之定时任务:自动清理日志,重启服务

    文章目录 背景 处理流程 查看进程 清理日志文件 重启服务 问题总结 完整处理方案 清理日志脚本 自动重启服务
  • ECU Bootloader程序开发

    文章目录 前言 前言 本篇主要介绍ECU BootLoader的 一般 开发逻辑 对正在 或想要做这方面工作的小伙伴一个参考 BootLoader的稳定性至关重要 前期设计要规避可能存在的所有使ECU变 砖 的风险点 尽可能地设计多重防范机
  • Transformer

    目录 Encoder LayerNorm怎么做的 Feed Forward FeedForward代码 公式 FeedForward的作用是什么 Decoder output 它的output 是什么 为什么output要进行shifted
  • 钛及钛合金材料的性质和应用-科学指南针

    一 钛的性质 这类气体是对金属钛十分有害的杂质 即使含量甚微 0 01 0 005 也能严重影响它的力学性能 钛的力学性质即通称的机械性能与纯度十分相关 高纯钛具有优良的机加工性能 延伸率 断面收缩率均佳 但强度低 不适合作结构材料 工业纯
  • 八爪鱼RPA机器人抖音达人合作邀约沟通

    让沟通变得简单粗暴的抖音达人合作邀约沟通rpa机器人 在当前的抖音达人合作邀约沟通行业中 存在着许多问题 首先 沟通过程中常常出现信息不准确 传达不清晰的情况 导致合作双方产生误解 影响合作的顺利进行 其次 沟通的过程中需要花费大量的时间和
  • 照片拼图软件哪些值得推荐?制作创意拼贴画

    你是否厌倦了单调的照片展示方式 你希望让你的照片更加有趣 更具创意吗 这时候你就需要一个拼图软件了 这种软件可以帮助你将多张照片拼贴在一起 从而制作出独特的拼贴画 通过这种软件 你可以轻松地调整照片大小 位置和角度 实现你想要的创意效果 同
  • 数说CS | 拟录取名额稳定,直博减少?保研清华大学软件学院难吗?

    写在前面 清华大学软件学院 首批全国示范性软件学院之一 招收哪些专业 保研录取情况如何 今天 岛主就带你 深度揭秘清华大学软件学院 01 院校介绍 清华大学软件学院是2001年经国家教育部和国家计委联合发文批准成立的首批全国示范性软件学院之
  • 手机话筒的安全性:风险与防范措施

    随着科技的快速发展和人们生活节奏的加快 手机已经成为我们日常生活中不可或缺的一部分 然而 手机话筒作为手机的主要部件之一 其安全性问题却往往被人们忽视 本文将围绕手机话筒的安全性进行探讨 分析可能存在的风险 并提出相应的防范措施 一 手机话
  • 2024 信息安全专业毕业设计(论文)选题合集 最新版

    目录 前言 信息安全选题 选题迷茫 选题的重要性 更多选题指导 最后 前言 大四是整个大学期间最忙碌的时光 一边要忙着备考或实习为毕业后面临的就业升学做准备 一边要为毕业设计耗费大量精力 近几年各个学校要求的毕设项目越来越难 有不少课题是研
  • 网站漏洞怎么修复代码漏洞

    jeecms 最近被爆出高危网站漏洞 可以导致网站被上传webshell木马文件 受影响的版本是jeecms V6 0版本到jeecmsV7 0版本 该网站系统采用的是JAVA语言开发 数据库 gt https cloud tencent
  • OpenHarmony 设备启动Logo和启动视频替换指南

    前言 OpenHarmony源码版本 4 0release 开发板 DAYU rk3568 一 Logo替换 替换其中的logo bmp 和 logo kernel bmp文件 注意事项 1 图片的分辨率需要和设备匹配 2 如果是非首次编译
  • 文字配音怎么制作的?让文字生动起来

    文字配音工具是一种将文字转化为语音的软件 它能够让文字以声音的形式呈现给听众 这种工具通常被用于阅读电子书 听新闻 看文章等方面 也可以帮助视觉障碍者更好地理解文字内容 文字配音工具一般具有多种语音选项和声音效果 可以让使用者自由选择适合自
  • 活动报名|JARVIS-1:开放世界自主智能体的构建

    王子豪 北京大学智能学院博士生 导师为梁一韬教授 曾获国家奖学金 北京市优秀毕业生等荣誉 主要研究方向为开放世界下多任务智能体的构建 尤其关心基于基础模型的智能体的泛化能力 近年来在CVPR NeurIPS等人工智能顶会上发表多篇论文 曾获
  • 多逆变器三相并网下垂控制仿真模型(Simulink仿真实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Simulink仿真实现
  • 钛合金的检测项目和标准-科学指南针

    根据国际商业市场的分析与预测 到2023年 钛合金的前景将会非常光明 可能会呈现指数增长 增长幅度较大 其实早在2017年 MRFR就指出2017 2023年这一时期内 钛合金的市场一定是保持稳定的增长 4月份 国际商业市场预测发布了 全球
  • 深度学习--环境搭建

    1 选择合适的操作系统 选择一个适合你的需求和喜好的 Linux 操作系统 比如 Ubuntu CentOS 等 此处以Ubuntu为例 2 安装anaconda创建虚拟环境 下载Anaconda安装程序 此处使用清华镜像源 wget c
  • 波奇学C++:C++11的可变参数模板和emplace

    可变参数模板 args是参数包 template
  • X射线荧光分析仪的原理及应用-科学指南针

    X射线荧光分析仪简称XRF 是分析XRF光谱分析技术可用于确认物质里的特定元素 同时将其量化 发展历程 1895年伦琴发现X射线 1910年特征X射线光谱的发现 为X射线光谱学的建立奠定了基础 20世纪50年代商用X射线发射与荧光光谱仪的问
  • 时效性文件链接实现思路

    1 写在前面 之前在某个项目中 用户上传的文件 头像 视频 文档等等 是通过静态路径来访问的 这导致一旦该文件的路径暴露 用户可以在不登录的情况下 直接访问服务器的文件资源 客户因此提出 文件的路径必须要具有时效性 类似对象存储的文件链接