Refactor thumbnail processing to utilize ThumbnailService for improved image handling and srcset generation

John Niang 2025-09-29 16:24:26 +08:00
parent 230550d0df
commit 8c8ff4c1ea
No known key found for this signature in database
GPG Key ID: D7363C015BBCAA59
1 changed files with 29 additions and 62 deletions

View File

@ -3,21 +3,18 @@ package run.halo.app.core.attachment;
import static org.thymeleaf.templatemode.TemplateMode.HTML;
import java.net.URI;
import java.util.Objects;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.engine.ElementNames;
import org.thymeleaf.model.IAttribute;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.processor.element.MatchingElementName;
import org.thymeleaf.spring6.context.SpringContextUtils;
import org.thymeleaf.spring6.context.webflux.SpringWebFluxThymeleafRequestContext;
import reactor.core.publisher.Mono;
import run.halo.app.infra.ExternalUrlSupplier;
import run.halo.app.theme.dialect.ElementTagPostProcessor;
@ -30,8 +27,12 @@ class ThumbnailImgTagPostProcessor implements ElementTagPostProcessor {
private final ExternalUrlSupplier externalUrlSupplier;
public ThumbnailImgTagPostProcessor(ExternalUrlSupplier externalUrlSupplier) {
private final ThumbnailService thumbnailService;
public ThumbnailImgTagPostProcessor(ExternalUrlSupplier externalUrlSupplier,
ThumbnailService thumbnailService) {
this.externalUrlSupplier = externalUrlSupplier;
this.thumbnailService = thumbnailService;
this.matchingElementName =
MatchingElementName.forElementName(HTML, ElementNames.forHTMLName("img"));
}
@ -57,63 +58,29 @@ class ThumbnailImgTagPostProcessor implements ElementTagPostProcessor {
// get img tag
var imageUri = srcValue.get();
if (imageUri.isAbsolute()) {
// check if the uri is belonged to current site
var requestContext = SpringContextUtils.getRequestContext(context);
if (!(requestContext instanceof SpringWebFluxThymeleafRequestContext wrc)) {
log.debug("Skip processing img tag with absolute url: {}, "
+ "because the request context is not webflux", imageUri);
return Mono.empty();
}
var externalUri = externalUrlSupplier.get();
if (!externalUri.isAbsolute()) {
externalUri = wrc.getServerWebExchange().getRequest().getURI();
}
if (!Objects.equals(externalUri.getAuthority(), imageUri.getAuthority())) {
log.debug("""
Skip processing img tag with external absolute url: {} because \
the url does not belong to the current site\
""", imageUri);
return Mono.empty();
}
}
var path = imageUri.getPath();
if (!path.startsWith("/upload/")) {
log.debug("Skip processing img tag with non-upload path: {}", path);
return Mono.empty();
}
var fileSuffix = FilenameUtils.getExtension(imageUri.getPath());
if (!ThumbnailUtils.isSupportedImage(fileSuffix)) {
log.debug("Skip processing img tag with unsupported image suffix: {}", fileSuffix);
return Mono.empty();
}
// build thumbnails
var thumbnails = ThumbnailUtils.buildSrcsetMap(imageUri);
if (CollectionUtils.isEmpty(thumbnails)) {
log.debug("Skip processing img tag because the image is not supported: {}", imageUri);
return Mono.empty();
}
var modelFactory = context.getModelFactory();
if (!tag.hasAttribute("sizes")) {
tag = modelFactory.setAttribute(tag, "sizes", """
(max-width: 640px) 94vw, \
(max-width: 768px) 92vw, \
(max-width: 1024px) 88vw, \
min(800px, 85vw)\
""");
}
var srcset = thumbnails.keySet().stream()
.map(size -> {
var uri = thumbnails.get(size);
return uri + " " + size.getWidth() + "w";
return thumbnailService.get(imageUri)
.filter(Predicate.not(Map::isEmpty))
.map(thumbnails -> {
var modelFactory = context.getModelFactory();
var newTag = tag;
if (!newTag.hasAttribute("sizes")) {
newTag = modelFactory.setAttribute(newTag, "sizes", """
(max-width: 640px) 94vw, \
(max-width: 768px) 92vw, \
(max-width: 1024px) 88vw, \
min(800px, 85vw)\
""");
}
var srcset = thumbnails.keySet().stream()
.map(size -> {
var uri = thumbnails.get(size);
return uri + " " + size.getWidth() + "w";
})
.collect(Collectors.joining(", "));
newTag = modelFactory.setAttribute(newTag, "srcset", srcset);
return newTag;
})
.collect(Collectors.joining(", "));
tag = modelFactory.setAttribute(tag, "srcset", srcset);
return Mono.just(tag);
.defaultIfEmpty(tag);
}
}