在Spring Boot和Vue.js项目中实现文件压缩下载功能,主要思路是后端负责将多个文件压缩成ZIP包,前端负责触发下载并处理文件流。以下是具体的实现方案。
🔧 后端实现(Spring Boot)
后端需要完成的核心任务是接收文件ID列表,查询对应的文件路径,将它们压缩成ZIP文件,并通过HTTP响应返回给前端。
1. 添加依赖
首先,在pom.xml
中添加处理ZIP文件的依赖,推荐使用功能强大的zip4j
库。
xml
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>2.11.5</version>
</dependency>
2. 核心服务层代码
创建一个Service方法,用于根据文件ID列表生成ZIP文件。此方法会查询文件实体,构建压缩包内的目录结构,并将文件添加到ZIP中。
java
@Service
@Slf4j
public class FileDownloadService {
@Autowired
private SysDownloadCenterAttachmentMapper attachmentMapper;
@Autowired
private ScjtConfig scjtConfig; // 假设这个配置类包含文件上传的基础路径
/**
* 创建ZIP压缩文件
* @param ids 文件ID列表,用逗号分隔
* @return 生成的ZIP文件在服务器上的绝对路径
*/
private String createZipFile(String ids) {
// 将传入的ID字符串转换为整型数组
Integer[] idArray = Convert.toIntArray(ids);
List<String> allFilePaths = new ArrayList<>();
// 1. 收集所有需要压缩的文件路径
for (Integer id : idArray) {
// 查询该ID对应的所有附件信息
List<SysDownloadCenterAttachment> attachments = attachmentMapper.selectList(
new LambdaQueryWrapper<SysDownloadCenterAttachment>()
.eq(SysDownloadCenterAttachment::getDownloadCenterId, id));
if (!CollectionUtils.isEmpty(attachments)) {
// 将附件信息转换为完整的服务器文件路径
List<String> paths = attachments.stream().map(attachment -> {
String location = attachment.getLocation();
String fullPath = scjtConfig.getUploadPath() + location;
// 简单的路径补全逻辑,请根据你的实际存储结构调整
if (!fullPath.startsWith("D:")) {
fullPath = "D:" + fullPath; // 示例路径,请修改为你的实际存储路径
}
return fullPath;
}).collect(Collectors.toList());
allFilePaths.addAll(paths);
}
}
// 2. 创建ZIP文件并添加内容
ZipFile zipFile = new ZipFile("d:/temp_download_filename.zip"); // 指定临时ZIP文件路径
for (String filePath : allFilePaths) {
File fileToAdd = new File(filePath);
if (fileToAdd.exists()) {
try {
// 设置ZIP文件参数:压缩方法、级别等
ZipParameters parameters = new ZipParameters();
parameters.setCompressionMethod(CompressionMethod.DEFLATE);
parameters.setCompressionLevel(CompressionLevel.NORMAL);
// 可选:在ZIP包内按原始目录结构组织文件,或自定义文件夹结构
// 例如,设置文件在ZIP包内的路径和名称
parameters.setFileNameInZip(fileToAdd.getName());
// 将文件添加到ZIP包
zipFile.addFile(fileToAdd, parameters);
log.info("文件已成功添加到ZIP: {}", filePath);
} catch (ZipException e) {
log.error("添加文件到ZIP时出错: {}", filePath, e);
throw new RuntimeException("压缩文件失败", e);
}
} else {
log.warn("文件不存在,跳过: {}", filePath);
}
}
return zipFile.getFile().getAbsolutePath();
}
/**
* 供Controller调用的下载入口方法
*/
public ResponseEntity<Resource> downloadZip(String ids, HttpServletResponse response) throws FileNotFoundException {
String zipFilePath = createZipFile(ids);
File zipFile = new File(zipFilePath);
// 将文件包装成Spring的Resource对象(这里使用InputStreamResource)
InputStreamResource resource = new InputStreamResource(new FileInputStream(zipFile));
// 设置HTTP响应头,告诉浏览器这是一个需要下载的附件
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="download.zip"")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.contentLength(zipFile.length())
.body(resource);
}
}
3. 控制器(Controller)
提供REST API接口给前端调用。
less
@RestController
@RequestMapping("/api/file")
public class FileDownloadController {
@Autowired
private FileDownloadService fileDownloadService;
@PostMapping("/download-zip")
public ResponseEntity<Resource> downloadZip(@RequestParam String ids, HttpServletResponse response) {
try {
return fileDownloadService.downloadZip(ids, response);
} catch (FileNotFoundException e) {
throw new RuntimeException("ZIP文件未找到", e);
}
}
}
🎨 前端实现(Vue.js)
前端负责向后端接口发送请求,并将接收到的ZIP文件流保存为本地文件。
1. 安装依赖(可选)
如果前端也需要处理压缩(例如在浏览器端打包),可以安装jszip
和file-saver
。但对于本文描述的后端压缩方案,以下为最简实现。
npm install file-saver
2. 发起下载请求
创建一个方法来调用后端的下载接口。使用axios
(或你项目使用的HTTP客户端)时,关键是指定responseType: 'blob'
,以便正确处理二进制流响应。
xml
<template>
<div>
<button @click="handleBatchDownload">下载选中文件压缩包</button>
</div>
</template>
<script>
import axios from 'axios'; // 确保已安装axios
export default {
data() {
return {
selectedFileIds: [1, 2, 3] // 假设这是用户选中的文件ID列表
};
},
methods: {
async handleBatchDownload() {
try {
// 将文件ID数组转换为后端接口需要的格式,例如逗号分隔的字符串
const idsParam = this.selectedFileIds.join(',');
const response = await axios.post('/api/file/download-zip',
{ ids: idsParam }, // 请求参数
{
responseType: 'blob' // 必须设置,告知axios期望接收二进制数据
}
);
// 处理下载的文件流
if (response.data.size > 0) {
// 创建一个指向Blob对象的URL
const blobUrl = window.URL.createObjectURL(new Blob([response.data], { type: 'application/zip' }));
// 创建一个隐藏的<a>标签并触发点击下载
const link = document.createElement('a');
link.style.display = 'none';
link.href = blobUrl;
link.download = 'files.zip'; // 设置下载的文件名
document.body.appendChild(link);
link.click();
// 清理DOM和释放Blob URL,避免内存泄漏
document.body.removeChild(link);
window.URL.revokeObjectURL(blobUrl);
} else {
this.$message.error('没有找到可下载的文件');
}
} catch (error) {
console.error('下载失败:', error);
this.$message.error('文件下载失败,请重试');
}
}
}
};
</script>
💡 性能与注意事项
- 大文件与内存管理 :对于超大文件或极多文件的压缩,建议使用流式压缩(如
ZipOutputStream
)而非一次性加载所有文件到内存,避免内存溢出(OOM)。后端示例中创建临时文件的方式在文件很大时也需注意磁盘空间和IO性能。 - 临时文件清理 :后端生成的临时ZIP文件应在下载完成后或定期进行清理,例如使用
File.delete()
或在项目启动时清理旧的临时文件,以免占用过多磁盘空间。 - 错误处理:增强代码的健壮性,对文件不存在、网络异常等情况进行妥善处理,并给前端返回明确的错误信息。
- 安全考虑:确保对文件ID进行权限校验,防止用户通过篡改ID下载未经授权的文件。
通过以上步骤,你就可以在Spring Boot和Vue.js项目中实现一个完整且健壮的文件压缩下载功能了。