Refactor thumbnail URI handling to ensure proper ASCII encoding and improve srcset generation

feat/add-thumbnail-router
John Niang 2025-09-30 00:26:13 +08:00
parent 57602d60a2
commit b6ab744d15
No known key found for this signature in database
GPG Key ID: D7363C015BBCAA59
5 changed files with 28 additions and 11 deletions

View File

@ -54,7 +54,7 @@ public enum ThumbnailUtils {
.collect(Collectors.toMap(t -> t, t -> .collect(Collectors.toMap(t -> t, t ->
UriComponentsBuilder.fromUri(permalink) UriComponentsBuilder.fromUri(permalink)
.queryParam("width", t.getWidth()) .queryParam("width", t.getWidth())
.build() .build(true)
.toUri() .toUri()
)); ));
} }

View File

@ -36,7 +36,6 @@ import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.util.unit.DataSize; import org.springframework.util.unit.DataSize;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriUtils;
import reactor.core.Exceptions; import reactor.core.Exceptions;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -155,14 +154,14 @@ class LocalAttachmentUploadHandler implements AttachmentHandler {
attachment.setSpec(spec); attachment.setSpec(spec);
attachment.setStatus(new Attachment.AttachmentStatus()); attachment.setStatus(new Attachment.AttachmentStatus());
doGetPermalink(attachment).ifPresent(permalink -> { doGetPermalink(attachment).ifPresent(permalink ->
attachment.getStatus().setPermalink(permalink.toASCIIString()); attachment.getStatus().setPermalink(permalink.toASCIIString())
}); );
var thumbnailLinks = doGetThumbnailLinks(attachment); var thumbnailLinks = doGetThumbnailLinks(attachment);
var thumbnails = thumbnailLinks.keySet().stream() var thumbnails = thumbnailLinks.keySet().stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
ThumbnailSize::name, ThumbnailSize::name,
size -> thumbnailLinks.get(size).toString() size -> thumbnailLinks.get(size).toASCIIString()
)); ));
if (!thumbnails.isEmpty()) { if (!thumbnails.isEmpty()) {
attachment.getStatus().setThumbnails(thumbnails); attachment.getStatus().setThumbnails(thumbnails);
@ -319,11 +318,10 @@ class LocalAttachmentUploadHandler implements AttachmentHandler {
} }
var uriStr = annotations.get(Constant.URI_ANNO_KEY); var uriStr = annotations.get(Constant.URI_ANNO_KEY);
// the uriStr is encoded before. // the uriStr is encoded before.
uriStr = UriUtils.decode(uriStr, StandardCharsets.UTF_8);
var uri = UriComponentsBuilder.fromUri(externalUrl.get()) var uri = UriComponentsBuilder.fromUri(externalUrl.get())
// The URI has been encoded before, so there is no need to encode it again. // The URI has been encoded before, so there is no need to encode it again.
.path(uriStr) .path(uriStr)
.build() .build(true)
.toUri(); .toUri();
return Optional.of(uri); return Optional.of(uri);
} }

View File

@ -18,8 +18,10 @@ public class ThumbnailFinderImpl implements ThumbnailFinder {
@Override @Override
public Mono<String> gen(String uriStr, String size) { public Mono<String> gen(String uriStr, String size) {
return thumbnailService.get(URI.create(uriStr), ThumbnailSize.fromName(size)) return Mono.fromCallable(() -> URI.create(uriStr))
.map(URI::toASCIIString); .flatMap(uri -> thumbnailService.get(uri, ThumbnailSize.fromName(size)))
.map(URI::toASCIIString)
.onErrorReturn(IllegalArgumentException.class, uriStr);
} }
} }

View File

@ -1,8 +1,11 @@
package run.halo.app.core.attachment; package run.halo.app.core.attachment;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import java.net.URI;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -26,4 +29,17 @@ class ThumbnailUtilsTest {
assertFalse(ThumbnailUtils.isSupportedImage(MediaType.parseMediaType(mimeType))); assertFalse(ThumbnailUtils.isSupportedImage(MediaType.parseMediaType(mimeType)));
} }
@Test
void shouldBuildSrcSetWithUriWithSpecialCharacters() {
var permalink = URI.create("/中文.png").toASCIIString();
var srcsetMap = ThumbnailUtils.buildSrcsetMap(URI.create(permalink));
assertEquals("/%E4%B8%AD%E6%96%87.png?width=400",
srcsetMap.get(ThumbnailSize.S).toString());
assertEquals("/%E4%B8%AD%E6%96%87.png?width=800",
srcsetMap.get(ThumbnailSize.M).toString());
assertEquals("/%E4%B8%AD%E6%96%87.png?width=1200",
srcsetMap.get(ThumbnailSize.L).toString());
assertEquals("/%E4%B8%AD%E6%96%87.png?width=1600",
srcsetMap.get(ThumbnailSize.XL).toString());
}
} }

View File

@ -1,6 +1,7 @@
package run.halo.app.theme.finders.impl; package run.halo.app.theme.finders.impl;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -37,7 +38,7 @@ class ThumbnailFinderImplTest {
.expectNext("invalid uri") .expectNext("invalid uri")
.verifyComplete(); .verifyComplete();
verify(thumbnailService, times(0)).generate(any(), any()); verify(thumbnailService, never()).get(any(), any());
} }
@Test @Test