Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions server/src/main/resources/web/picture.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@
});
}

// 从原始图片 URL 提取文件名(优先用 data-original-url,避免受反代 / CORS 代理 URL 影响)
function getImageNameFromOriginalUrl(imageElement) {
var originalUrl = imageElement.getAttribute('data-original-url') || '';
// 去除查询串与锚点
var noQuery = originalUrl.split('?')[0].split('#')[0];
// 取路径最后一段作为文件名(兼容 / 与 \)
var lastSep = Math.max(noQuery.lastIndexOf('/'), noQuery.lastIndexOf('\\'));
var name = lastSep >= 0 ? noQuery.substring(lastSep + 1) : noQuery;
return name || 'image';
}

// 初始化图片查看器
function initImageViewer() {
var viewer = new Viewer(document.getElementById('image'), {
Expand All @@ -66,6 +77,10 @@
button: false,
backdrop: false,
loop: true,
// Viewer 会将克隆图片的 data-original-url 改为加载地址,因此从原始元素读取标题。
title: function () {
return getImageNameFromOriginalUrl(this.images[this.index]);
}
});
viewer.view(0); // 0 是图片的索引,如果你想点击第一张图片,索引为 0
}
Expand Down
52 changes: 52 additions & 0 deletions server/src/test/java/cn/keking/PictureFilePreviewTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package cn.keking;

import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* kkFileView #787 回归测试:直接图片预览(picture.ftl)必须在 Viewer.js 标题里显示文件名。
*
* 根因:v4.3.0 之后模板把图片元素从 <img> 改成 <div src>,而 HTMLDivElement 没有 DOM 的
* src 属性,Viewer.js 用 image.src 取标题得到 undefined,导致标题为空。修复方式是在 Viewer 初始化时
* 配置 title 函数,从 data-original-url 派生文件名。
*
* Viewer 会将内部克隆图片的 data-original-url 改为实际加载地址(可能是代理地址),
* 标题必须读取 this.images[this.index] 中保留原始 URL 的元素。
*/
public class PictureFilePreviewTests {

@Test
void shouldDeriveViewerTitleFromOriginalUrl() throws IOException {
String pictureTemplate = readResource("/web/picture.ftl");

// 模板仍保留 data-original-url 供标题派生使用
assertTrue(pictureTemplate.contains("data-original-url"),
() -> "picture.ftl must keep data-original-url for title derivation (see #787)");

// Viewer 初始化必须配置 title 函数,否则 <div> 的 image.src 为 undefined、标题为空
assertTrue(pictureTemplate.contains("title: function"),
() -> "picture.ftl must configure a Viewer title function to show the filename (see #787)");

// 标题派生逻辑必须读取 data-original-url(而非反代 / CORS 代理 URL)
assertTrue(pictureTemplate.contains("getImageNameFromOriginalUrl"),
() -> "picture.ftl must derive the title from data-original-url (see #787)");
assertTrue(pictureTemplate.contains("getAttribute('data-original-url')"),
() -> "title derivation must read data-original-url attribute (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 {
try (InputStream inputStream = getClass().getResourceAsStream(resourcePath)) {
assertNotNull(inputStream);
return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
}
}
}
14 changes: 14 additions & 0 deletions tests/e2e/specs/picture-title.spec.ts
Original file line number Diff line number Diff line change
@@ -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');
});
}
Loading