У меня есть реактивный API, который принимает Mono в качестве аргумента, как показано ниже.
Mono<FileType> processAndReturnType(Mono<FilePart> partFlux)
Мне нужно вызвать это из другого API, который принимает файл как
getType(MultipartFile partFile)
Вызов API не является реактивным, и как я могу преобразовать MultipartFile в FilePart.
Один из способов приведен ниже, который работает нормально, но вызывает проблемы безопасности/проблемы сонара, поскольку файл доступен во временном каталоге –
Мне интересно, что может быть лучшим способом справиться с этим. Если я смогу напрямую транслировать. Любое предложение поможет
package com.maersk.clm.naas.tenderfile.convertor;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.lang.NonNull;
import reactor.core.scheduler.Schedulers;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
/**
* Implementation of {@link FilePart} that wraps a {@link MultipartFile}.
*/
public class MultipartFilePart implements FilePart {
private final MultipartFile file;
public MultipartFilePart(MultipartFile file) {
this.file = file;
}
@Override
@NonNull
public Flux<DataBuffer> content() {
try {
Path tempFile = Files.createTempFile("upload", "temp");
file.transferTo(tempFile);
return DataBufferUtils.read(tempFile, new DefaultDataBufferFactory(), (int) file.getSize());
} catch (IOException e) {
return Flux.error(e);
}
}
@Override
@NonNull
public String filename() {
return Objects.requireNonNull(file.getOriginalFilename());
}
@Override
@NonNull
public String name() {
return file.getName();
}
@Override
@NonNull
public Mono<Void> transferTo(@NonNull Path dest) {
return DataBufferUtils.write(content(), dest)
.then()
.publishOn(Schedulers.boundedElastic())
.doFinally(signalType -> {
try {
Files.deleteIfExists(dest);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}
@Override
@NonNull
public HttpHeaders headers() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(Objects.requireNonNull(file.getContentType())));
return headers;
}
}
Я пробовал разные способы конвертации, но не нашел лучшего решения.
Можно обойтись и без временного файла. MultipartFile
имеет два других способа получить доступ к своему содержимому: getBytes()
возвращает byte[]
и getInputStream()
возвращает InputStream
. Вы можете использовать оба, чтобы получить Flux
.
DataBufferUtils
есть метод readInputStream, который прост в использовании:
return DataBufferUtils.readInputStream(
file::getInputStream,
new DefaultDataBufferFactory(),
(int) file.getSize());
С помощью getBytes()
вы можете использовать фабрику напрямую:
return Flux.just(new DefaultDataBufferFactory().wrap(file.getBytes());
Из этих трёх вариантов я бы выбрал readInputStream
. Временный файл включает дисковый ввод-вывод, и файл необходимо удалить, а вы забыли это сделать. Опция byte[]
должна синхронно прочитать все содержимое в памяти, прежде чем его можно будет обернуть. С другой стороны, опция readInputStream
может работать даже с меньшим размером буфера.
Как только вы это заработаете, я посмотрю, можно ли создать многоразовый DataBufferFactory
. DefaultDataBufferFactory.sharedInstance
может быть вариант.