fix: 修复水印配置缺失时 xlsx 预览白页 - #789
yuhaibohotmail wants to merge 1 commit into
Conversation
未提供外部 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>
klboke
left a comment
There was a problem hiding this comment.
已核对当前提交 c4e1929 的模板、配置加载和请求属性链路。水印配置缺失导致 FreeMarker 渲染中断的根因成立,采用模板缺省值作为局部修复也合理。不过还有一个已复现的 locale 问题需要在合并前修复,详见行内评论。
验证情况:
- 本地运行
OfficeWebWatermarkDefaultsTests,2 条测试通过;以-DargLine="-Duser.language=de -Duser.country=DE"再运行也通过,说明现有断言未覆盖默认透明度的错误输出。 - 使用项目实际依赖中的 Spring
FreeMarkerView、模拟请求/响应及AcceptHeaderLocaleResolver渲染当前模板,并对生成的内联脚本运行node --check:缺少全部水印属性时,zh-CN通过,de-DE、fr-FR、ar-EG失败;提供完整水印配置时均通过。 - 在临时模板中将六个数值缺省值改为字符串后,上述四种语言的缺失配置和已有配置场景均通过 JavaScript 语法检查,已有配置值保持原样。
这次复测验证的是模板渲染和生成脚本的语法,不依赖 LibreOffice 或完整服务启动。
| watermark_font: '${watermarkFont!'微软雅黑'}', | ||
| watermark_fontsize: '${watermarkFontsize!'18px'}', | ||
| watermark_color: '${watermarkColor!'black'}', | ||
| watermark_alpha: ${watermarkAlpha!0.2}, |
There was a problem hiding this comment.
[P2] 数值缺省值需要避免 locale 格式化,否则仍会导致 xlsx 预览失败
当水印属性缺失、请求携带 Accept-Language: de-DE 或 fr-FR 时,${watermarkAlpha!0.2} 的缺省值是 FreeMarker 数值,会被渲染为:
watermark_alpha: 0,2,我通过 Spring FreeMarkerView 复现了该输出,node --check 报 SyntaxError: Unexpected number。此时响应为 200,HTML 也包含 </html>,但第一个内联脚本块无法解析,initWaterMark 和 isLoading 都没有初始化;后面的 loadTextAsync() 在访问 isLoading 时仍然无法继续加载。因此仅检查页面完整性不能证明这个场景已修复。ar-EG 下其他数值缺省值也会输出本地化数字,造成语法错误。
建议将这六个数值缺省值统一改成字符串,让它们与 Java 侧 DEFAULT_* 的类型一致,并直接输出合法的 JavaScript 数字文本:
watermark_x_space: ${watermarkXSpace!'10'},
watermark_y_space: ${watermarkYSpace!'10'},
watermark_alpha: ${watermarkAlpha!'0.2'},
watermark_width: ${watermarkWidth!'240'},
watermark_height: ${watermarkHeight!'80'},
watermark_angle: ${watermarkAngle!'10'},这个调整已在临时模板中复测通过,并且不会覆盖已存在的配置。请同时补上 de_DE / ar_EG 下缺失水印属性的回归测试,检查所有数值输出或生成脚本的语法;当前缺省值测试只断言了空文本、宽度和字体,因此会漏掉这个错误。
问题
在未提供外部
config/application.properties的部署上,预览任何 xlsx 都失败(5.0.2 与当前 master 都有)。页面从
<html>开始正常输出、断在半截 JavaScript 里、永远没有</html>,浏览器上表现为白页或一直转圈。HTTP 状态码是 200,服务端日志里只有一句「响应已提交、错误页渲染不了」——它长得像转换超时,不像模板错,
所以很容易一路去查 LibreOffice 和转换链路。
真正的报错是:
原因
两处叠在一起,缺一个都不会出事:
1. 十个
watermark*请求属性可能整族不存在。WatermarkConfigConstants没有任何类级注解(对比ConfigConstants上的@Component),所以它不是 Spring bean,那十个
@Value("${watermark.xxx:默认值}")的 setter 从来不会被调用。唯一会填充那十个静态字段的是
ConfigRefreshComponent#loadConfig(),而它在配置文件不存在时直接return:此时十个 getter 全部返回
null,而AttributeSetFilter的request.setAttribute(name, null)按 Servlet 规范等同于删除属性——不是「值是 null」,是「这个属性不存在」。
2.
officeweb.ftl是唯一没有classic_compatible兜底的预览模板。commonHeader.ftl第 1 行是<#setting classic_compatible=true>,而这个设置作用于整个渲染环境、覆盖到 include 它的那份模板后续的引用(已单独用 FreeMarker 验过)。于是 csv / markdown / pdf / picture 等
所有 include 它的模板在属性缺失时把变量渲染成空串、照常出页面;
officeweb.ftl既不 include 它、自己也没有这个设置,所以只有 xlsx 预览会硬失败。修复
给
officeweb.ftl里那十个变量补上 FreeMarker 缺省值,取的就是WatermarkConfigConstants.DEFAULT_*那一组(
10 / 10 / 微软雅黑 / 18px / black / 0.2 / 240 / 80 / 10),不另编一套。watermarkTxt取空串——空串会让模板下一行的if (watermarkTxt !== '')跳过watermark.init(),不会给所有预览凭空加上水印。其余九个只在水印真的开着时才用得上,但那个
if是 JavaScript 的、不是模板的,FreeMarker 两条分支都会渲染,所以十个都要给(只补
watermarkTxt的话,页面会改为断在下一个变量
watermarkXSpace上,症状一模一样)。验证
新增
server/src/test/java/cn/keking/web/OfficeWebWatermarkDefaultsTests.java,直接用 FreeMarker渲染这个模板——不需要把服务跑起来,也不需要 LibreOffice:
shouldRenderXlsxPreviewPageWhenWatermarkAttributesAreMissing:数据模型里一个watermark*都不给(那正是缺陷发生时的情形),断言渲染得出
</html>、watermarkTxt落到空串、其余落到声明的缺省值。⚠ 判据取「有没有
</html>」而不是 HTTP 200——坏的时候响应头早就发出去了,也是 200。shouldKeepConfiguredWatermarkSettingsWhenAttributesArePresent:十个值全给上,断言逐个原样渲染,保证缺省值不会盖掉真实配置。
改模板之前先跑过一遍:第一条报
InvalidReferenceException(第 25 行watermarkTxt),第二条通过——说明红的确实是被测的那个条件,不是模型缺了别的东西。改完之后:
没有一并改的
commonHeader.ftl与pdf.ftl里也是裸${watermark*},今天靠classic_compatible=true兜着,没跟着改——改动越小越好审。
null:给WatermarkConfigConstants加@Component(让它的
@Value缺省值真正生效),或让 getter 回落到DEFAULT_*。那会动到配置装载的语义,超出这个 PR 的范围;如果维护者更倾向那条路,我可以照着改成那一版。
server/src/main/config/application.properties里watermark.width默认是 180,而WatermarkConfigConstants.DEFAULT_WATERMARK_WIDTH是 240。本 PR 取的是后者(Java 侧声明的默认值)。