【问题标题】:ResponseEntity<InputStreamResource> return two service methodsResponseEntity<InputStreamResource> 返回两个服务方法
【发布时间】:2021-01-27 04:22:17
【问题描述】:

我有两个服务返回两个不同的 ResponseEntity。

public ResponseEntity<InputStreamResource> getA(...) {
return ResponseEntity
            .ok()
            .headers(headers)
            .contentType(MediaType.APPLICATION_PDF)
            .contentLength(out.size())
            .body(new InputStreamResource(bis)); }
public ResponseEntity<InputStreamResource> getB(...) {
return ResponseEntity
            .ok()
            .headers(headers)
            .contentType(MediaType.APPLICATION_PDF)
            .contentLength(out.size())
            .body(new InputStreamResource(bis)); }

每个服务都有一个调用和返回的控制器。

public ResponseEntity<InputStreamResource> getA(...) {
return aService.getA(...) }
public ResponseEntity<InputStreamResource> getB(...) {
return bService.getB(...) }

我正在尝试创建另一个控制器,它同时执行并返回两个服务。

public ResponseEntity<InputStreamResource> getAB(...) {
return aService.getA(...) *and* bService.getB(...) ?????? }

不确定如何将两个 ResponseEntities 返回合并为一个。

【问题讨论】:

    标签: java spring controller


    【解决方案1】:
    import org.springframework.core.io.InputStreamResource;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import com.itextpdf.text.Document;
    import com.itextpdf.text.PageSize;
    import com.itextpdf.text.pdf.PdfCopy;
    import com.itextpdf.text.pdf.PdfReader;
    
    @RestController
    public class ReportController {
    private static final Logger LOGGER = ...
    
    @Autowired
    private ReportManager manager;
    
    @GetMapping("/reportcard/students")
    public ResponseEntity<InputStreamResource> getStudentReportCard(parameters...) throws ServiceException {
        List<InputStream> studentReportList = manager.getStudentReports(parameters.... );
                
        HttpHeaders headers = getHeaders(Constants.STUDENTS_REPORT_CARD);
    
        Document document = new Document(PageSize.LETTER);
        ByteArrayOutputStream outputStream = null;
    
        try {
            outputStream = new ByteArrayOutputStream();
            PdfCopy copy = new PdfCopy(document, outputStream);
    
            document.open();
    
            for (InputStream file : studentReportList) {
                copy.addDocument(new PdfReader(file)); // writes directly to the output stream
            }
            outputStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (document.isOpen()) {
                document.close();
            }
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }
    
        InputStreamResource inputStreamResource = new InputStreamResource(new ByteArrayInputStream(outputStream.toByteArray()));
        return ResponseEntity.ok().headers(headers).contentType(MediaType.APPLICATION_PDF).body(inputStreamResource);
    }
    
    private HttpHeaders getHeaders(String fileName) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
        headers.add("Pragma", "no-cache");
        headers.add("Expires", "0");
        headers.set("Content-disposition", "attachment; filename=" + fileName);
        return headers;
    }
    
    
    public class ReportManagerImpl implements ReportManager {
    private static final Logger LOGGER = ...
    
    @Autowired
    private HttpClient httpClient;
    
    @Override
    public List<InputStream> getStudentReports(parameters...)
            throws ServiceException {
        
        List<InputStream> studentReportList = new Vector<InputStream>();
        
        List<String> learnersList = //list of students
        
        String reportUrl = //url
            
        for (String learner : learnersList) {
            studentReportList.add(getHttpResponse(reportUrl + learner));
        }
        
        return studentReportList;
    }
    
    private InputStream getHttpResponse(String url) throws ServiceException {
        try {
            HttpResponse response = httpClient.execute(new HttpGet(url));
            HttpEntity entity = response.getEntity();
    
            return entity.getContent();
        } catch (IOException e) {
            throw new ServiceException(e.getMessage());
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      从 Service 方法返回 ResponseEntity 不是一个好主意。

      Controller 层应该负责生成 ResponseEntity 对象。那是他的事,不是服务层的事。同样,服务层的职责是根据给定的输入准备某种 DTO 对象,然后控制器将环绕该 DTO 并将其作为响应发送。

      所以,我建议在这里做一些结构上的改变。

      服务层

      public InputStreamResource getA(...) {
        return A 
      }
      public InputStreamResource getB(...) {
        return B 
      }
      

      控制器层

      public ResponseEntity<InputStreamResource> getA(...) {
        return new ResponseEntity<>(aService.getA(...) (
      }
      public ResponseEntity<InputStreamResource> getB(...) {
        return new ResponseEntity<>(bService.getB(...) )
      }
      

      合并 2 个流


      如果您的目标是逐个流式传输 2 个不同的 pdf 文档,那么我认为可以先将内存中的 pdf 文档与您可能使用的任何 pdf 库合并。然后创建一个 InputStreamResource 作为 Response。

      但是,如果流可以按顺序运行,那么下面是一个使用 SequenceInputStream 合并 2 个流的工作示例 -

      @RequestMapping(
              path = "/sayHello",
              method = RequestMethod.GET,
              produces = MediaType.TEXT_PLAIN_VALUE
      )
      public ResponseEntity<InputStreamResource> get() {
              byte[] inputBytes1 = "Hello".getBytes();
              // 1st stream has "Hello" text
              ByteArrayInputStream baos1 = new ByteArrayInputStream(inputBytes1);
      
              byte[] inputBytes2 = "World".getBytes();
              // 2nd stream has "World" text
              ByteArrayInputStream baos2 = new ByteArrayInputStream(inputBytes2);
      
              // combined stream will have "HelloWorld" text
              SequenceInputStream combinedStream = new SequenceInputStream(baos1, baos2);
              InputStreamResource inputStreamResource = new InputStreamResource(combinedStream);
              return ResponseEntity.ok().body(inputStreamResource);
          }
      

      输出 ->

       curl -X GET  http://localhost:8083/sayHello
       HelloWorld
      

      【讨论】:

      • ResponseEntity 有效。 ResponseEntity 不起作用。
      • @user12527433 什么不起作用?有什么例外吗?您能否发布该异常或问题。
      • HttpMessageNotReadableException getA 和 getB 返回 InputStreamResource 类型,但 getAB 返回对象 CombinedInputStreamDTO 类型
      • 感谢您的帮助。现在....使用sequenceinputstream,我只能生成baos2。好像是 baos1 被覆盖了。
      • @user12527433 如果是字符串流合并(如示例中所示),则可以使用 SequenceInputStream。这按预期工作。但是,既然你想合并 pdf 流,看看这是否对你有用 - stackoverflow.com/questions/3585329/…
      【解决方案3】:

      您可以通过这种方式尝试:

      public ResponseEntity<List<InputStreamResource> getAAndB(...) {
          
                        private List<InputStreamResource> result = new ArrayList<>();
                        result.add(aService.getA(...));
                        result.add(bService.getB(...));
                 return ResponseEntity.ok().body(result)); }  
          }
      

      【讨论】:

        猜你喜欢
        • 2020-04-22
        • 1970-01-01
        • 2022-01-06
        • 1970-01-01
        • 2020-05-23
        • 2018-04-02
        • 2018-09-15
        • 1970-01-01
        • 2012-04-28
        相关资源
        最近更新 更多