Как читать и записывать файлы реактивным способом, используя InputStreamand OutputStream - PullRequest
0 голосов
/ 18 января 2019

Я пытаюсь прочитать файл Excel, манипулировать им или добавить новые данные в него и записать его обратно. Я также пытаюсь сделать это полным реактивным процессом, используя Flux и Mono. Идея состоит в том, чтобы вернуть полученный файл или байтовый массив через веб-сервис.

У меня вопрос, как мне получить InputStream и OutputStream неблокирующим способом?

Я использую библиотеку Apache Poi для чтения и создания файла Excel.

В настоящее время у меня есть решение, основанное на сочетании Mono.fromCallable () и кода блокировки, получающего входной поток.

Например, часть веб-сервиса выглядит следующим образом.

@GetMapping(value = API_BASE_PATH + "/download", produces = "application/vnd.ms-excel")
public Mono<ByteArrayResource> download() {
    Flux<TimeKeepingEntry> createExcel = excelExport.createDocument(false);

    return createExcel.then(Mono.fromCallable(() -> {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        excelExport.getWb().write(outputStream);
        return new ByteArrayResource(outputStream.toByteArray());
    }).subscribeOn(Schedulers.elastic()));
}

И обработка файла:

public Flux<TimeKeepingEntry> createDocument(boolean all) {
    Flux<TimeKeepingEntry> entries = null;
    try {
        InputStream inputStream = new ClassPathResource("Timesheet Template.xlsx").getInputStream();
        wb = WorkbookFactory.create(inputStream);
        Sheet sheet = wb.getSheetAt(0);

        log.info("Created document");

        if (all) {
            //all entries
        } else {
            entries = service.findByMonth(currentMonthName).log("Excel Export - retrievedMonths").sort(Comparator.comparing(TimeKeepingEntry::getDateOfMonth)).doOnNext(timeKeepingEntry-> {
                this.populateEntry(sheet, timeKeepingEntry);
            });
        }
    } catch (IOException e) {
        log.error("Error Importing File", e);
    }
    return entries;
}

Это работает достаточно хорошо, но не совсем соответствует Flux и Mono. Некоторое руководство здесь было бы хорошо. Я бы предпочел, чтобы вся последовательность была неблокируемой.

1 Ответ

0 голосов
/ 18 января 2019

К сожалению, операция WorkbookFactory.create() блокируется, поэтому вы должны выполнить эту операцию, используя императивный код. Однако получение каждый раз KeepEntry может быть выполнено реактивно. Ваш код будет выглядеть примерно так:

public Flux<TimeKeepingEntry> createDocument() {
    return Flux.generate(
        this::getWorkbookSheet,
        (sheet, sink) -> {
            sink.next(getNextTimeKeepingEntryFrom(sheet));
        },
        this::closeWorkbook);
}

Это сохранит рабочую книгу в памяти, но будет извлекать каждую запись по требованию, когда запрашиваются элементы Flux.

...