StreamingResponseBody 简介
Spring框架中,StreamingResponseBody 是一个接口,它允许我们以流的方式写入HTTP响应体。这种方式非常适合处理大文件下载、大批量数据导出等场景,因为它可以避免一次性加载所有数据到内存中,从而减少内存占用并提高性能。
使用 StreamingResponseBody 的主要优势包括
-
流式传输:数据可以分块地发送给客户端,而不是等待整个文件准备完毕。
-
低内存占用:只在需要时加载数据,并立即发送给客户端,减少了对服务器内存的压力。
-
更好的用户体验:用户无需长时间等待,文件开始生成后即可逐步接收到内容。
我们将采用StreamingResponseBody来实现流式响应,同时结合分页查询和异步处理。以下是带有详细注释的优化实现
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
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 org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
@RestController
@RequestMapping("/orders")
public class OptimizedOrderController {
@Autowired
private OrderService orderService;
/**
* 处理订单导出请求,使用StreamingResponseBody实现流式响应。
* 这样做可以确保即使有大量的订单数据也不会导致内存溢出,
* 同时可以让用户更快地开始接收文件。
*/
@GetMapping("/export")
public ResponseEntity<StreamingResponseBody> exportOrders() {
// 创建StreamingResponseBody实例,该对象将在每次有新数据可写时被调用
StreamingResponseBody stream = outputStream -> {
int pageSize = 1000; // 每页查询的数量
int pageNumber = 0; // 分页起始页码
boolean hasMoreData = true; // 是否还有更多数据标志
try {
// 循环直到所有数据都被处理完毕
while (hasMoreData) {
// 构造分页请求参数
PageRequest pageRequest = PageRequest.of(pageNumber++, pageSize, Sort.by("id"));
// 执行分页查询
Page<Order> page = orderService.findPage(pageRequest);
// 遍历当前页的数据,并逐行写入输出流
for (Order order : page.getContent()) {
writeOrderToExcelRow(order, outputStream);
}
// 更新是否有更多数据的标志
hasMoreData = page.hasNext();
}
} catch (IOException e) {
// 如果发生IO异常,记录日志或采取其他措施
e.printStackTrace();
}
};
// 设置HTTP头信息,告知浏览器这是一个附件下载
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=orders.xlsx");
// 返回包含StreamingResponseBody的ResponseEntity对象
return ResponseEntity.ok()
.headers(headers)
.contentType(MediaType.APPLICATION_OCTET_STREAM) // 设置响应的内容类型为二进制流
.body(stream); // 将StreamingResponseBody作为响应体
}
/**
* 将单个订单转换成一行Excel格式,并写入到提供的输出流中。
* 注意:这里简化了实际的Excel写入逻辑,具体实现依赖于所使用的库(如Apache POI)。
*/
private void writeOrderToExcelRow(Order order, OutputStream outputStream) throws IOException {
// 实现将单个订单写入Excel文件的一行
// 此处省略具体实现细节...
}
}