Java服务端如何为生成的zip压缩包生成临时随机下载链接
实现临时随机下载链接的方案
首先你现有代码的时序存在问题:当前生成zip包后直接执行了删除逻辑,就算生成了下载链接,用户访问时文件已经被清理,无法完成下载,需要先调整文件删除的触发时机。
完整实现步骤如下:
- 第一步:生成唯一不可预测的随机token
可以用UUID或者安全随机数生成字符串,避免被恶意遍历下载,示例代码:String token = UUID.randomUUID().toString().replace("-", ""); - 第二步:存储token关联信息
把token、zip文件存储路径、过期时间(可自行设置,比如15分钟有效期)存入存储介质:- 小型单实例项目可以直接用内存的
ConcurrentHashMap做临时存储 - 分布式项目推荐用Redis存储,可直接设置key的自动过期时间,无需手动清理过期记录
- 小型单实例项目可以直接用内存的
- 第三步:拼接生成下载链接
拼接你的服务域名、下载接口路径和token参数即可得到最终的下载链接 - 第四步:实现单独的下载处理接口
新增接口处理下载请求,核心逻辑为:- 接收请求携带的token,先查询对应记录是否存在、是否已过期
- 校验通过后,读取对应zip文件的流返回给客户端
- 下载完成后,删除zip文件和对应的token记录;如果到了过期时间还未被访问,也自动删除对应文件和记录
代码示例
修改后的链接生成方法
// 这里用ConcurrentHashMap做临时存储示例,生产环境按需换成Redis等共享存储 private static final ConcurrentHashMap<String, FileMeta> tokenStore = new ConcurrentHashMap<>(); // 下载链接有效期15分钟,单位毫秒 private static final long EXPIRE_TIME = 15 * 60 * 1000; // 存储文件关联信息的实体类 class FileMeta { String filePath; long expireAt; public FileMeta(String filePath, long expireAt) { this.filePath = filePath; this.expireAt = expireAt; } } public String getDownloadLink(String[] files) throws IOException { // [...] 原有生成Download文件夹的逻辑 // 生成zip包,这里文件名也用随机名,避免多并发请求的文件冲突 String zipFileName = UUID.randomUUID().toString().replace("-", "") + ".zip"; File zipFile = new File(zipFileName); ZipUtil.pack(new File("Download"), zipFile); // 生成随机token String token = UUID.randomUUID().toString().replace("-", ""); // 存储token和文件的关联信息 tokenStore.put(token, new FileMeta(zipFile.getAbsolutePath(), System.currentTimeMillis() + EXPIRE_TIME)); // 只删除临时Download文件夹,zip包暂时保留到过期或下载完成 FileUtils.deleteDirectory(new File("Download")); // 拼接下载链接,替换为你自己的服务地址和接口路径 String downloadLink = "https://你的服务域名/api/download/" + token; return downloadLink; }
SpringBoot 下载接口示例
@GetMapping("/api/download/{token}") public void downloadFile(@PathVariable String token, HttpServletResponse response) throws IOException { // 校验token是否有效 FileMeta fileMeta = tokenStore.get(token); if (fileMeta == null || System.currentTimeMillis() > fileMeta.expireAt) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); return; } File zipFile = new File(fileMeta.filePath); if (!zipFile.exists()) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); tokenStore.remove(token); return; } // 设置下载响应头 response.setContentType("application/zip"); response.setHeader("Content-Disposition", "attachment; filename=\"selected-files.zip\""); // 输出文件流 try (FileInputStream fis = new FileInputStream(zipFile); OutputStream os = response.getOutputStream()) { byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { os.write(buffer, 0, len); } os.flush(); } finally { // 下载完成后删除文件和token记录 zipFile.delete(); tokenStore.remove(token); } }
注意事项
- 多实例部署的场景不要用本地内存存储token,要改用Redis等共享存储
- 可以额外设置下载次数限制,比如只允许下载一次,避免链接泄露后被多人下载
- 随机token建议用足够长的安全随机字符串,不要用自增数字等可预测的内容,避免被遍历爬取文件
内容的提问来源于stack exchange,提问作者Kass224
相关产品推荐
相关产品推荐

