Итак, весь мой процесс выглядит следующим образом:
@RequiredArgsConstructor
class ImageCommandProcessingService {
private final DownloadRequestFactory downloadRequestFactory;
private final ImageClientDownloader imageClientDownloader;
private final ImageResizeRequestFactory imageResizeRequestFactory;
private final ImageResizeService imageResizeService;
Mono<List<ImageResizeResult>> process(ResizeImageCommand resizeImageCommand) {
return Mono.just(resizeImageCommand)
.map(command -> downloadRequestFactory.create(command.getImageUrl().getUrl()))
.flatMap(imageClientDownloader::downloadImage)
.map(downloadedImage -> imageResizeRequestFactory.createRequests(downloadedImage, resizeImageCommand.getSizes().toJavaList()))
.flatMap(imageResizeService::resize);
}
}
У меня есть команда с URL-адресом изображения и набором размеров:
@Value
class ResizeImageCommand {
private ImageUrl imageUrl;
private Set<ImageSize> sizes;
}
Сначала мне нужно загрузить изображениена диске, поэтому я создаю запрос на загрузку по фабрике:
@RequiredArgsConstructor
class DownloadRequestFactory {
private final ImageLocationPathResolver resolver;
DownloadRequest create(String url) {
return new DownloadRequest(url, resolver.resolveDownloadedLocation(url));
}
}
Resolver - это класс, отвечающий за создание пути к временному файлу и за создание пути для измененного образа:
class ImageLocationPathResolver {
private String temporaryImagesFolder;
private String destinationImagesFolder;
Path resolveDownloadedLocation(String imageUrl) {
LocalDateTime now = LocalDateTime.now();
String fileName = now.toString() + "_" + getFileNameExtensionFromUrl(imageUrl);
return Paths.get(temporaryImagesFolder,getDatePaths(now.toLocalDate()), fileName);
}
Path resolveDestinationLocation(ImageSize imageSize, String url) {
String fileName = getFileNameExtensionFromUrl(url);
return Paths.get(destinationImagesFolder, imageSize.getName(), getDatePaths(LocalDate.now()), fileName);
}
private String getFileNameExtensionFromUrl(String url) {
return StringUtils.getFilenameExtension(url);
}
private String getDatePaths(LocalDate now) {
return now.getYear() + File.pathSeparator + now.getMonth() + File.pathSeparator + now.getDayOfMonth();
}
}
Далее у меня есть клиент, отвечающий за операцию загрузки:
public interface ImageClientDownloader {
Mono<DownloadedImage> downloadImage(DownloadRequest downloadRequest);
}
и реализацию:
@Slf4j
class HttpImageClientDownloader implements ImageClientDownloader {
private final WebClient webClient;
HttpImageClientDownloader() {
this.webClient = WebClient.create();
}
@Override
public Mono<DownloadedImage> downloadImage(DownloadRequest downloadRequest) {
try {
Flux<DataBuffer> dataBuffer = webClient.get()
.uri(downloadRequest.getUrl())
.retrieve()
.bodyToFlux(DataBuffer.class);
Path resultFilePath = Files.createFile(downloadRequest.getLocation());
WritableByteChannel channel = Files.newByteChannel(resultFilePath, StandardOpenOption.WRITE);
return DataBufferUtils.write(dataBuffer, channel)
.map(DataBufferUtils::release)
.then(Mono.just(new DownloadedImage(downloadRequest.getUrl(), resultFilePath, LocalDateTime.now())));
} catch (Exception e) {
log.error(e.getMessage(), e);
return Mono.error(e);
}
}
}
Это операция ввода-вывода.Должен ли я использовать выделенный планировщик? В конце у меня есть операция изменения размера, запрос создается внутри операции карты - imageResizeRequestFactory.