From 86c2b8d552c93a10e18ef40babcc51f63195efab Mon Sep 17 00:00:00 2001 From: kl Date: Thu, 8 Oct 2026 13:02:39 +0800 Subject: [PATCH] fix: retain original image titles when proxying previews --- server/src/main/resources/web/picture.ftl | 13 +++---------- .../java/cn/keking/PictureFilePreviewTests.java | 14 ++++---------- tests/e2e/specs/picture-title.spec.ts | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 20 deletions(-) create mode 100644 tests/e2e/specs/picture-title.spec.ts diff --git a/server/src/main/resources/web/picture.ftl b/server/src/main/resources/web/picture.ftl index 94e52795..85464130 100644 --- a/server/src/main/resources/web/picture.ftl +++ b/server/src/main/resources/web/picture.ftl @@ -77,16 +77,9 @@ button: false, backdrop: false, loop: true, - // #787: Viewer.js 的 title 回调拿到的 `image` 是它内部新建的 (见 viewer.min.js - // 的 view(): `var image = document.createElement('img')`),并不是原始
。Viewer - // 在构建缩略图与查看大图时只透传 `inheritedAttributes` 里的属性,而默认值不含 - // data-original-url,所以若不显式加入,getImageNameFromOriginalUrl 在该 上永远 - // getAttribute('data-original-url') == null,标题会恒为 'image'(修复形同虚设)。 - // 因此必须把 data-original-url 加进 inheritedAttributes,让原始 URL 随缩略图→大图透传。 - inheritedAttributes: ['crossOrigin', 'decoding', 'isMap', 'loading', 'referrerPolicy', 'sizes', 'srcset', 'useMap', 'data-original-url'], - // 从 data-original-url 派生标题文件名(避免受反代 / CORS 代理 URL 影响) - title: function (image, imageData) { - return getImageNameFromOriginalUrl(image); + // Viewer 会将克隆图片的 data-original-url 改为加载地址,因此从原始元素读取标题。 + title: function () { + return getImageNameFromOriginalUrl(this.images[this.index]); } }); viewer.view(0); // 0 是图片的索引,如果你想点击第一张图片,索引为 0 diff --git a/server/src/test/java/cn/keking/PictureFilePreviewTests.java b/server/src/test/java/cn/keking/PictureFilePreviewTests.java index 9be91c44..823377a3 100644 --- a/server/src/test/java/cn/keking/PictureFilePreviewTests.java +++ b/server/src/test/java/cn/keking/PictureFilePreviewTests.java @@ -16,10 +16,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; * src 属性,Viewer.js 用 image.src 取标题得到 undefined,导致标题为空。修复方式是在 Viewer 初始化时 * 配置 title 函数,从 data-original-url 派生文件名。 * - * 关键点(已核对 viewer.min.js 源码):title 回调拿到的 `image` 是 Viewer 内部新建的 <img> - * (view(): `var image = document.createElement('img')`),不是原始 <div>。Viewer 在构建缩略图 - * 与查看大图时只透传 `inheritedAttributes` 里的属性,默认值不含 data-original-url,所以必须把 - * data-original-url 加进 inheritedAttributes,否则 title 函数永远读不到它、标题恒为 'image'。 + * Viewer 会将内部克隆图片的 data-original-url 改为实际加载地址(可能是代理地址), + * 标题必须读取 this.images[this.index] 中保留原始 URL 的元素。 */ public class PictureFilePreviewTests { @@ -41,12 +39,8 @@ public class PictureFilePreviewTests { assertTrue(pictureTemplate.contains("getAttribute('data-original-url')"), () -> "title derivation must read data-original-url attribute (see #787)"); - // 关键:Viewer.js 只会把 inheritedAttributes 透传到 title 回调拿到的 上,默认值不含 - // data-original-url。若不显式加入,title 函数读不到原始 URL、标题恒为 'image'(修复形同虚设)。 - assertTrue(pictureTemplate.contains("inheritedAttributes"), - () -> "picture.ftl must set Viewer inheritedAttributes so data-original-url reaches the title image (see #787)"); - assertTrue(pictureTemplate.contains("'data-original-url'"), - () -> "Viewer inheritedAttributes must include data-original-url (see #787)"); + assertTrue(pictureTemplate.contains("getImageNameFromOriginalUrl(this.images[this.index])"), + () -> "Viewer title must use the original element rather than its proxy-backed clone (see #787)"); } private String readResource(String resourcePath) throws IOException { diff --git a/tests/e2e/specs/picture-title.spec.ts b/tests/e2e/specs/picture-title.spec.ts new file mode 100644 index 00000000..aff9b6fa --- /dev/null +++ b/tests/e2e/specs/picture-title.spec.ts @@ -0,0 +1,14 @@ +import { test, expect } from '@playwright/test'; + +const fixtureBase = process.env.FIXTURE_BASE_URL || 'http://127.0.0.1:18080'; + +for (const proxy of [false, true]) { + test(`image title retains the source filename with kkagent=${proxy}`, async ({ page }) => { + const source = `${fixtureBase}/sample.png`; + const encoded = encodeURIComponent(Buffer.from(source).toString('base64')); + const response = await page.goto(`/onlinePreview?url=${encoded}&kkagent=${proxy}`); + + expect(response?.status()).toBe(200); + await expect(page.locator('.viewer-title')).toHaveText('sample.png'); + }); +}