mirror of
https://gitee.com/kekingcn/file-online-preview.git
synced 2026-11-11 13:50:44 +00:00
fix: 水印配置缺失时 xlsx 预览白页 —— officeweb.ftl 补上缺省值
未提供外部 config/application.properties 的部署上,预览任何 xlsx 都失败: FreeMarker 在 officeweb.ftl 第 25 行抛 InvalidReferenceException,点名 watermarkTxt。DEBUG 模式把错误内联进 HTML,页面断在半截 JavaScript 里、 永远没有 </html>,浏览器上是白页或一直转圈,而 HTTP 状态码是 200 —— 看起来像转换超时,不像模板错。 两处叠在一起: 1. 十个 watermark* 请求属性可能整族不存在。WatermarkConfigConstants 没有 类级注解(对比 ConfigConstants 上的 @Component),不是 Spring bean, 那十个 @Value 的 setter 从来不会被调用;唯一填充它们的 ConfigRefreshComponent#loadConfig() 在配置文件不存在时直接 return。 于是十个 getter 全返回 null,AttributeSetFilter 的 setAttribute(name, null) 按 Servlet 规范等同于删除属性。 2. officeweb.ftl 是唯一没有 classic_compatible 兜底的预览模板。 commonHeader.ftl 第 1 行是 <#setting classic_compatible=true>, 所有 include 它的模板在属性缺失时把变量渲染成空串、照常出页面; officeweb.ftl 既不 include 它、自己也没有这个设置。 缺省值取 WatermarkConfigConstants.DEFAULT_*,watermarkTxt 取空串—— 空串会让下一行的 if (watermarkTxt !== '') 跳过 watermark.init(),不会给 所有预览凭空加上水印。其余九个只在水印开着时才用到,但那个 if 是 JavaScript 的、不是模板的,FreeMarker 两条分支都渲染,所以十个都要给。 新增的测试直接用 FreeMarker 渲染这个模板,不需要把服务跑起来: 数据模型里一个 watermark* 都不给(那正是缺陷发生时的情形),断言渲染得出 </html>;另一条把十个值全给上,断言逐个原样渲染,保证缺省值不会盖掉真实配置。 改模板之前先跑过一遍,第一条报 InvalidReferenceException、第二条通过。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,23 +22,23 @@
|
||||
* 初始化水印
|
||||
*/
|
||||
function initWaterMark() {
|
||||
let watermarkTxt = '${watermarkTxt}';
|
||||
let watermarkTxt = '${watermarkTxt!''}';
|
||||
if (watermarkTxt !== '') {
|
||||
watermark.init({
|
||||
watermark_txt: '${watermarkTxt}',
|
||||
watermark_txt: '${watermarkTxt!''}',
|
||||
watermark_x: 0,
|
||||
watermark_y: 0,
|
||||
watermark_rows: 0,
|
||||
watermark_cols: 0,
|
||||
watermark_x_space: ${watermarkXSpace},
|
||||
watermark_y_space: ${watermarkYSpace},
|
||||
watermark_font: '${watermarkFont}',
|
||||
watermark_fontsize: '${watermarkFontsize}',
|
||||
watermark_color: '${watermarkColor}',
|
||||
watermark_alpha: ${watermarkAlpha},
|
||||
watermark_width: ${watermarkWidth},
|
||||
watermark_height: ${watermarkHeight},
|
||||
watermark_angle: ${watermarkAngle},
|
||||
watermark_x_space: ${watermarkXSpace!10},
|
||||
watermark_y_space: ${watermarkYSpace!10},
|
||||
watermark_font: '${watermarkFont!'微软雅黑'}',
|
||||
watermark_fontsize: '${watermarkFontsize!'18px'}',
|
||||
watermark_color: '${watermarkColor!'black'}',
|
||||
watermark_alpha: ${watermarkAlpha!0.2},
|
||||
watermark_width: ${watermarkWidth!240},
|
||||
watermark_height: ${watermarkHeight!80},
|
||||
watermark_angle: ${watermarkAngle!10},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package cn.keking.web;
|
||||
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateExceptionHandler;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* officeweb.ftl is the only preview template that neither includes commonHeader.ftl nor
|
||||
* turns on classic_compatible, so it is the only one that fails outright when the
|
||||
* watermark request attributes are absent.
|
||||
*
|
||||
* They are absent whenever WatermarkConfigConstants has not been populated: it is not a
|
||||
* Spring bean, so its @Value defaults never run, and ConfigRefreshComponent#loadConfig
|
||||
* returns early when the external config/application.properties is missing. Every getter
|
||||
* then returns null, and AttributeSetFilter's setAttribute(name, null) removes the
|
||||
* attribute per the Servlet spec.
|
||||
*
|
||||
* The first test therefore renders with a model that carries no watermark* entry at all.
|
||||
* Do not add them "to make it realistic" - that is the condition under test.
|
||||
*/
|
||||
class OfficeWebWatermarkDefaultsTests {
|
||||
|
||||
@Test
|
||||
void shouldRenderXlsxPreviewPageWhenWatermarkAttributesAreMissing() throws Exception {
|
||||
String html = render(baseModel());
|
||||
|
||||
assertTrue(html.contains("</html>"), "page was truncated before </html>");
|
||||
assertTrue(html.contains("let watermarkTxt = '';"), "watermarkTxt did not fall back to an empty string");
|
||||
assertTrue(html.contains("watermark_width: 240,"), "watermarkWidth did not fall back to its declared default");
|
||||
assertTrue(html.contains("watermark_font: '\u5fae\u8f6f\u96c5\u9ed1',"), "watermarkFont did not fall back to its declared default");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldKeepConfiguredWatermarkSettingsWhenAttributesArePresent() throws Exception {
|
||||
Map<String, Object> model = baseModel();
|
||||
model.put("watermarkTxt", "kkFileView");
|
||||
model.put("watermarkXSpace", "20");
|
||||
model.put("watermarkYSpace", "30");
|
||||
model.put("watermarkFont", "Arial");
|
||||
model.put("watermarkFontsize", "24px");
|
||||
model.put("watermarkColor", "red");
|
||||
model.put("watermarkAlpha", "0.5");
|
||||
model.put("watermarkWidth", "300");
|
||||
model.put("watermarkHeight", "120");
|
||||
model.put("watermarkAngle", "45");
|
||||
|
||||
String html = render(model);
|
||||
|
||||
assertTrue(html.contains("let watermarkTxt = 'kkFileView';"));
|
||||
assertTrue(html.contains("watermark_x_space: 20,"));
|
||||
assertTrue(html.contains("watermark_y_space: 30,"));
|
||||
assertTrue(html.contains("watermark_font: 'Arial',"));
|
||||
assertTrue(html.contains("watermark_fontsize: '24px',"));
|
||||
assertTrue(html.contains("watermark_color: 'red',"));
|
||||
assertTrue(html.contains("watermark_alpha: 0.5,"));
|
||||
assertTrue(html.contains("watermark_width: 300,"));
|
||||
assertTrue(html.contains("watermark_height: 120,"));
|
||||
assertTrue(html.contains("watermark_angle: 45,"));
|
||||
}
|
||||
|
||||
/** Everything officeweb.ftl needs except the watermark attributes. */
|
||||
private Map<String, Object> baseModel() {
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
model.put("file", Map.of("name", "demo.xlsx"));
|
||||
model.put("pdfUrl", "demo.xlsx");
|
||||
model.put("baseUrl", "http://127.0.0.1:8012/");
|
||||
model.put("kkagent", "false");
|
||||
model.put("kkkey", "");
|
||||
model.put("xlsxshowtoolbar", Boolean.TRUE);
|
||||
model.put("xlsxallowEdit", Boolean.FALSE);
|
||||
return model;
|
||||
}
|
||||
|
||||
private String render(Map<String, Object> model) throws Exception {
|
||||
Configuration configuration = new Configuration(Configuration.VERSION_2_3_32);
|
||||
configuration.setClassLoaderForTemplateLoading(getClass().getClassLoader(), "web");
|
||||
configuration.setDefaultEncoding("UTF-8");
|
||||
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
|
||||
|
||||
Template template = configuration.getTemplate("officeweb.ftl");
|
||||
StringWriter out = new StringWriter();
|
||||
template.process(model, out);
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user