簡體   English   中英

How to write java.util.stream.Stream to StreamingResponseBody output stream

[英]How to write java.util.stream.Stream to StreamingResponseBody output stream

我正在構建一個 REST API ,其中來自 Oracle 數據庫的大量數據可以通過流式傳輸到客戶端應用程序(如文件下載或直接流)以塊的形式發送

我從 JpaRepository 獲得 Stream,如下所示 -

@Query("select u from UsersEntity u")
Stream<UsersEntity> findAllByCustomQueryAndStream();

但是現在挑戰將這個 stream 寫入 StreamingResponseBody Output stream

我嘗試了很多方法但沒有成功 -

第一種方法-

Stream<UsersEntity> usersResultStream = usersRepository.findAllByCustomQueryAndStream();

        StreamingResponseBody stream = outputStream -> {
            Iterator<UsersEntity> iterator = usersResultStream.iterator();

            try (ObjectOutputStream oos = new ObjectOutputStream(outputStream)) {

                while (iterator.hasNext()) {
                    oos.write(iterator.next().toString().getBytes());
                }
            }
        };

出現錯誤-

java.sql.SQLException: Closed Resultset: next
    at oracle.jdbc.driver.InsensitiveScrollableResultSet.next(InsensitiveScrollableResultSet.java:565) ~[ojdbc7-12.1.0.2.jar:12.1.0.2.0]

第二種方法-

StreamingResponseBody stream = new StreamingResponseBody() {

            @Transactional(readOnly = true)
            @Override
            public void writeTo(OutputStream outputStream) throws IOException {

                Stream<UsersEntity> usersResultStream = usersRepository.findAllByCustomQueryAndStream();

                try (ObjectOutputStream oos = new ObjectOutputStream(outputStream)) {

                    usersResultStream.forEach(user->{
                        try {
                            oos.write(user.toString().getBytes());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    });
                }
            }
        }; 

出現錯誤-

org.springframework.dao.InvalidDataAccessApiUsageException: You're trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed. Make sure the code consuming the stream uses @Transactional or any other way of declaring a (read-only) transaction.

我已經在下面給出的鏈接上傳了練習代碼 - 示例 POC 鏈接

我對流媒體相關任務沒有任何經驗,所以請幫助我。

如果我的方向錯誤,則建議在Spring Framework中執行此操作。 如果可用,請分享任何參考鏈接。

最后,我通過使用服務層解決了這個問題。 最初,我在 Controller Class 中編寫了完整的邏輯,這導致了這個問題。

Controller Class -

@RestController
@RequestMapping("/api")
public class UsersController {
    @Autowired
    private UserService service;

    @GetMapping(value = "/userstream")
    public ResponseEntity<StreamingResponseBody> fetchUsersStream() {

        StreamingResponseBody stream = this::writeTo;

        return new ResponseEntity<>(stream, HttpStatus.OK);
    }

    private void writeTo(OutputStream outputStream) {
        service.writeToOutputStream(outputStream);
    }
}

服務 Class -

@Service
public class UserService {

    @Autowired
    private UsersRepository usersRepository;

    @Transactional(readOnly = true)
    public void writeToOutputStream(final OutputStream outputStream) {
        try (Stream<UsersEntity> usersResultStream = usersRepository.findAllByCustomQueryAndStream()) {
            try (ObjectOutputStream oos = new ObjectOutputStream(outputStream)) {

                usersResultStream.forEach(emp -> {
                    try {
                        oos.write(emp.toString().getBytes());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

完整代碼可在 github - https://github.com/bagesh2050/HttpResponseStreamingDemo

不過,我願意提供有關 Http 流媒體的建議。 如果您有更好的想法,請提供。

沒有示例顯示StreamingResponseBody的“如此復雜”的用法,我擔心這是“不可能的”(至少我無法使用 StreamingResponseBodyStream 查詢來管理/修復它)

...但是,有什么可能:

  1. 在 StreamingResponseBody 中使用findAll() (正常的非流式 List-repo 方法)。

    (但我理解異步執行 web 請求的“需要”......並且數據庫請求“流式傳輸”......)

  2. 使用Callable (異步 web 請求)和@Async CompletableFuture<..> (異步數據庫請求):

     @RestController @RequestMapping("/api") public class UsersController { @Autowired private UsersRepository usersRepository; @GetMapping(value = "/async/users") public Callable<List<UsersEntity>> fetchUsersAsync() { Callable callable = () -> { return usersRepository.readAllBy().get(); }; return callable; } }

    ..和像這樣的存儲庫:

     @Repository public interface UsersRepository extends JpaRepository<UsersEntity, Integer> { @Async CompletableFuture<List<UsersEntity>> readAllBy(); }

    (請參閱spring-samples )..不要忘記在您的應用程序/配置上@EnableAsync

     @org.springframework.scheduling.annotation.EnableAsync @SpringBootApplication public class Application {... }

抱歉,這甚至不是答案,而是我的發現——評論太長了。

異步 web 請求可以通過多種方式實現。 (見https://spring.io/blog/2012/05/10/spring-mvc-3-2-preview-making-a-controller-method-asynchronous/,https ://niels.nu/blog/2016 /spring-async-rest.html ,甚至沒有提到“反應式”api)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM