You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Spring Boot后端向Angular12前端传输就绪的ExecutionDto列表?

问题描述

我有大量的Execution实体数据,需要处理后转换为ExecutionDto,Dto定义如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ExecutionDto {
    private long OrgUnitId;
    private long countsDone;
    private long countsShouldBe;
    private float progressPercentage;
    private double countsIndicator;
    private String OrgUnitDisplayName;
}

目前处理方法是一次性生成完整的Dto列表:

public List<ExecutionDTO> processExecutions(){
    // 处理逻辑,生成完整的ExecutionDto列表
    return listOfExecutions;
}

对应的REST接口一次性返回整个列表:

@GetMapping("/executions/process")
public ResponseEntity<List<ExecutionDTO>> processedExecutions(){
    List<ExecutionDTO> executionDTOs = executionService.processExecutions();
    return ResponseEntity.ok().body(executionDTOs);
}

现在前端需要等待后端生成完所有Dto才能拿到数据,耗时极长。我不想用分页,希望将生成好的Dto实时传输到前端,而非等待整个列表处理完成,请问该怎么实现?

解决方案

要实现实时流式传输,核心是让后端边处理边返回数据,而非等待全量数据生成完毕。Spring框架提供两种主流实现方式:

1. 使用Spring MVC的StreamingResponseBody(Servlet栈)

适合传统Spring MVC项目,通过分块传输编码(Chunked Transfer Encoding)分批发送数据。

改造Service方法

将批量处理改为流式迭代处理,避免一次性加载所有数据到内存:

public Stream<ExecutionDTO> processExecutionsStream(){
    // 示例:从数据库流式查询Execution,边查边转换Dto
    return executionRepository.streamAll() // 假设Repository支持流式查询
                              .map(this::convertToDto);
}

private ExecutionDTO convertToDto(Execution execution){
    ExecutionDTO dto = new ExecutionDTO();
    dto.setOrgUnitId(execution.getOrgUnitId());
    dto.setCountsDone(execution.getCountsDone());
    dto.setCountsShouldBe(execution.getCountsShouldBe());
    dto.setProgressPercentage(execution.getProgressPercentage());
    dto.setCountsIndicator(execution.getCountsIndicator());
    dto.setOrgUnitDisplayName(execution.getOrgUnitDisplayName());
    return dto;
}

改造REST接口

使用StreamingResponseBody逐个输出Dto,采用NDJSON(换行分隔JSON)格式:

@GetMapping(value = "/executions/stream", produces = MediaType.APPLICATION_NDJSON_VALUE)
public ResponseEntity<StreamingResponseBody> streamExecutions(){
    Stream<ExecutionDTO> dtoStream = executionService.processExecutionsStream();
    StreamingResponseBody responseBody = outputStream -> {
        ObjectMapper mapper = new ObjectMapper();
        try (dtoStream) { // 自动关闭流
            dtoStream.forEach(dto -> {
                try {
                    mapper.writeValue(outputStream, dto);
                    outputStream.write('\n'); // 用换行分隔单个Dto
                    outputStream.flush(); // 立即推送数据到前端
                } catch (IOException e) {
                    throw new RuntimeException("写入Dto到流失败", e);
                }
            });
        }
    };
    return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_NDJSON_VALUE)
            .body(responseBody);
}

2. 使用Spring WebFlux的Flux(响应式栈)

如果项目基于响应式编程,直接返回Flux<ExecutionDTO>即可,框架自动处理流式传输。

改造Service方法

返回Flux类型实现异步流式处理:

public Flux<ExecutionDTO> processExecutionsFlux(){
    // 响应式Repository流式查询,例如MongoDB的findAll()返回Flux<Execution>
    return executionRepository.findAll()
                              .map(this::convertToDto);
}

改造REST接口

直接返回Flux<ExecutionDTO>:

@GetMapping(value = "/executions/flux", produces = MediaType.APPLICATION_NDJSON_VALUE)
public Flux<ExecutionDTO> fluxExecutions(){
    return executionService.processExecutionsFlux();
}

前端处理示例

前端需监听响应数据流,逐行解析处理单个Dto:

fetch('/executions/stream')
  .then(response => {
    const reader = response.body.getReader();
    const decoder = new TextDecoder('utf-8');
    let buffer = '';

    function processChunk() {
      return reader.read().then(({ done, value }) => {
        if (done) {
          console.log('数据流传输完成');
          return;
        }
        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n');
        buffer = lines.pop(); // 保留未完成的行

        lines.forEach(line => {
          if (line.trim()) {
            const dto = JSON.parse(line);
            // 处理单个Dto:比如渲染到页面、添加到列表
            console.log('收到Dto:', dto);
          }
        });
        return processChunk();
      });
    }

    return processChunk();
  })
  .catch(error => console.error('数据流接收失败:', error));

内容的提问来源于stack exchange,提问作者amir raad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 12:15:47