小明:最近我在开发一个高校人事管理系统,里面需要实现文件下载的功能。你知道该怎么实现吗?
小李:嗯,这个问题挺常见的。首先你要确定下载的是什么类型的文件,比如是Excel、PDF还是Word文档?然后要处理好文件的路径和权限问题。
小明:对了,我用的是Spring Boot框架,有没有什么好的方法可以快速实现这个功能?
小李:当然有啦。你可以使用Spring MVC中的ResponseEntity来返回文件流,或者用Resource来处理静态资源。
小明:那具体怎么写代码呢?能给我举个例子吗?
小李:当然可以。比如说,你想从服务器下载一个Excel文件,那么后端的Controller可以这样写:
@RestController
public class FileDownloadController {
@GetMapping("/download")
public ResponseEntity downloadFile() {
// 假设文件存储在服务器的某个位置
String filePath = "D:/files/example.xlsx";
Path path = Paths.get(filePath);
Resource resource = new UrlResource(path.toUri());
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
}
}
小明:这个代码看起来很清晰,但如果是动态生成的文件呢?比如用户点击下载时才生成Excel数据?
小李:这种情况下,你需要在Controller里动态生成文件内容,然后把它转换成字节数组,再通过ResponseEntity返回。
小明:那应该怎么处理呢?能给我看看代码示例吗?
小李:好的,下面是一个动态生成Excel并下载的例子:
@RestController
public class DynamicFileDownloadController {
@GetMapping("/dynamic-download")
public ResponseEntity downloadDynamicExcel() throws Exception {
// 模拟一些数据
List users = Arrays.asList(
new User("张三", "123456"),
new User("李四", "789012")
);
// 创建Excel文件
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (Workbook workbook = new XSSFWorkbook()) {
Sheet sheet = workbook.createSheet("用户列表");
Row headerRow = sheet.createRow(0);
Cell headerCell = headerRow.createCell(0);
headerCell.setCellValue("姓名");
headerCell = headerRow.createCell(1);
headerCell.setCellValue("学号");
for (int i = 0; i < users.size(); i++) {
Row row = sheet.createRow(i + 1);
Cell cell = row.createCell(0);
cell.setCellValue(users.get(i).getName());
cell = row.createCell(1);
cell.setCellValue(users.get(i).getStudentId());
}
workbook.write(outputStream);
}
byte[] excelBytes = outputStream.toByteArray();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "users.xlsx");
return new ResponseEntity<>(excelBytes, headers, HttpStatus.OK);
}
}
小明:哦,原来如此!那前端应该怎么调用这个接口呢?
小李:前端可以用fetch或者axios发起GET请求,然后处理响应的Blob数据,最后触发浏览器下载。
小明:具体的前端代码是怎样的?

小李:这里是一个简单的示例,使用JavaScript来下载文件:
async function downloadFile() {
const response = await fetch('/dynamic-download');
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'users.xlsx';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}
小明:明白了,这样就能实现动态生成并下载文件了。
小李:没错。不过要注意,如果文件较大,可能要考虑分页或异步生成,避免内存溢出。
小明:那如果我要支持多文件下载,或者让用户选择多个文件一起下载呢?
小李:这时候你可以考虑使用ZIP压缩包。后端将多个文件打包成一个ZIP文件,再返回给前端下载。
小明:那这个过程复杂吗?能不能也给个例子?
小李:不复杂,下面是一个简单的示例代码:
@GetMapping("/zip-download")
public ResponseEntity downloadZip() throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (ZipOutputStream zipOut = new ZipOutputStream(outputStream)) {
// 添加第一个文件
addFileToZip(zipOut, "file1.txt", "这是第一个文件的内容".getBytes());
// 添加第二个文件
addFileToZip(zipOut, "file2.txt", "这是第二个文件的内容".getBytes());
}
byte[] zipBytes = outputStream.toByteArray();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "files.zip");
return new ResponseEntity<>(zipBytes, headers, HttpStatus.OK);
}
private void addFileToZip(ZipOutputStream zipOut, String fileName, byte[] data) throws IOException {
ZipEntry zipEntry = new ZipEntry(fileName);
zipOut.putNextEntry(zipEntry);
zipOut.write(data);
zipOut.closeEntry();
}
小明:这个例子太棒了!看来只要合理设计,下载功能可以非常灵活。
小李:没错,而且你还可以结合Spring Security来控制下载权限,确保只有授权用户才能访问某些文件。
小明:那如果我要在下载前进行权限验证呢?

小李:可以在Controller的方法上加@PreAuthorize注解,或者在方法内部检查用户角色。
小明:好的,那我得好好研究一下Spring Security的相关配置。
小李:对了,还有一个点需要注意,就是文件路径的安全性。不要直接使用用户输入的路径,否则可能会引发安全漏洞。
小明:明白了,应该用白名单机制或者加密后的路径来处理。
小李:没错。总之,下载功能虽然看似简单,但在实际开发中需要考虑很多细节,包括性能、安全性、用户体验等。
小明:谢谢你,这些内容对我帮助很大!
小李:不客气,希望你能顺利实现你的项目!如果有其他问题,随时来找我。
