小李:最近公司要开发一个员工宿舍管理系统,我负责其中的下载功能模块,但对具体怎么实现不太清楚,你能帮我分析一下吗?
老张:当然可以。首先,你得明确下载功能的需求是什么。比如,用户需要下载哪些数据?是宿舍分配信息、入住记录,还是其他类型的文件?
小李:主要是宿舍分配信息和入住记录的Excel表格。用户可以在系统中选择时间范围或宿舍编号,然后点击下载按钮,生成对应的Excel文件。
老张:明白了。那我们可以用Java来实现这个功能,后端使用Spring Boot框架,结合Apache POI库来生成Excel文件。
小李:Apache POI?是那个用来处理Excel文件的库吗?
老张:没错。Apache POI是一个强大的Java库,可以创建和操作Excel文件。我们可以通过它来动态生成Excel表格,并将其返回给前端。
小李:那具体该怎么实现呢?有没有示例代码可以参考?
老张:当然有。下面是一段简单的代码示例,展示如何在Spring Boot中实现下载功能。
package com.example.housing.controller;
import com.example.housing.service.HousingService;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
@RestController
@RequestMapping("/api/housing")
public class HousingDownloadController {
@Autowired
private HousingService housingService;
@GetMapping("/download")
public ResponseEntity downloadExcel() throws IOException {
List data = housingService.getAllHousingInfo();
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("宿舍信息");
// 创建表头
Row headerRow = sheet.createRow(0);
Cell headerCell1 = headerRow.createCell(0);
headerCell1.setCellValue("宿舍编号");
Cell headerCell2 = headerRow.createCell(1);
headerCell2.setCellValue("员工姓名");
Cell headerCell3 = headerRow.createCell(2);
headerCell3.setCellValue("入住日期");
// 填充数据
for (int i = 0; i < data.size(); i++) {
Row row = sheet.createRow(i + 1);
row.createCell(0).setCellValue(data.get(i).getRoomNumber());
row.createCell(1).setCellValue(data.get(i).getEmployeeName());
row.createCell(2).setCellValue(data.get(i).getCheckInDate().toString());
}
// 将Workbook写入字节数组
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
workbook.write(outputStream);
workbook.close();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "housing_info.xlsx");
return ResponseEntity.ok()
.headers(headers)
.body(outputStream.toByteArray());
}
}
小李:这段代码看起来不错,但我有点不明白的是,为什么使用ResponseEntity来返回Excel文件?
老张:因为ResponseEntity允许我们直接控制HTTP响应的内容和头信息。这样可以直接将生成的Excel文件作为二进制流返回给前端,用户点击下载时就能得到正确的文件。

小李:明白了。那前端怎么调用这个接口呢?是不是要用AJAX或者直接跳转到URL?
老张:通常有两种方式。一种是前端通过AJAX请求下载链接,获取到文件后使用Blob对象进行下载;另一种是直接在浏览器中打开下载链接,让浏览器自动处理。
小李:那前端应该怎么写?有没有示例代码?
老张:当然有。下面是一个简单的前端JavaScript示例,使用fetch API发起GET请求并触发下载。
function downloadExcel() {
fetch('/api/housing/download')
.then(response => response.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'housing_info.xlsx';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
})
.catch(error => console.error('下载失败:', error));
}
小李:这太好了!那如果用户需要根据条件筛选数据再下载呢?比如按时间范围或宿舍编号过滤?
老张:这个问题可以通过在后端添加参数来解决。比如,前端传入startTime、endTime、roomNumber等参数,后端根据这些参数查询数据,然后生成对应的Excel文件。
小李:那后端的代码应该怎么修改呢?
老张:我们可以修改@GetMapping的路径,使其支持查询参数。例如,将原来的@GetMapping("/download")改为@GetMapping("/download"),并在方法中接收这些参数。
小李:那具体的代码应该怎么写?
老张:下面是一个修改后的代码示例:
@GetMapping("/download")
public ResponseEntity downloadExcel(
@RequestParam(required = false) String startTime,
@RequestParam(required = false) String endTime,
@RequestParam(required = false) String roomNumber) throws IOException {
List data = housingService.getFilteredHousingInfo(startTime, endTime, roomNumber);
// 后续代码同上...
}
小李:明白了。那HousingService中的getFilteredHousingInfo方法应该怎么实现呢?
老张:这个方法可以根据传入的参数构建查询条件,从数据库中获取符合条件的数据。例如,使用JPA或MyBatis来执行查询。
小李:那如果数据量很大,会不会影响性能?
老张:确实可能会。如果数据量非常大,建议分页加载,或者优化查询语句。此外,还可以考虑异步生成Excel文件,避免阻塞主线程。
小李:那异步生成怎么实现?
老张:可以使用Spring的@Async注解,将生成Excel的任务放到线程池中执行。这样前端可以先收到一个任务ID,之后通过轮询或WebSocket获取生成结果。
小李:听起来很高级。不过对于小型项目来说,可能不需要这么复杂。
老张:没错。根据项目的规模和需求,可以选择合适的方案。如果你现在只是做一个简单的下载功能,目前的代码已经足够用了。
小李:好的,那我现在就按照这个思路去实现吧。谢谢你的帮助!
老张:不客气,有问题随时问我。祝你开发顺利!
