Skip to content

fix: guard null key in redis image cache lookup - #794

Open
ccinos wants to merge 1 commit into
kekingcn:masterfrom
ccinos:fix/redis-imgcache-nullkey
Open

ccinos wants to merge 1 commit into
kekingcn:masterfrom
ccinos:fix/redis-imgcache-nullkey

Conversation

@ccinos

@ccinos ccinos commented Sep 20, 2026

Copy link
Copy Markdown

Fixes #793

问题

cache.type=redis 时,图片预览必然返回 HTTP 500:

java.lang.NullPointerException: map key can't be null
    at org.redisson.RedissonMap.checkKey(RedissonMap.java:448)
    ...
    at cn.keking.service.cache.impl.CacheServiceRedisImpl.getImgCache(CacheServiceRedisImpl.java:72)
    at cn.keking.service.impl.PictureFilePreviewImpl.filePreviewHandle(PictureFilePreviewImpl.java:34)

PictureFilePreviewImpl 在非压缩包场景下把 fileAttribute.getCompressFileKey()(此时为 null)
直接传给了 FileHandlerService#getImgCache,而 redis 实现在取缓存前没有判空,
Redisson 4.x 对 null key 会抛 NPE。

改动

只动 1 个文件、7 行:server/src/main/java/cn/keking/service/cache/impl/CacheServiceRedisImpl.java

+import org.apache.commons.lang3.StringUtils;
+import java.util.ArrayList;
+
     @Override
     public List<String> getImgCache(String key) {
+        if (StringUtils.isEmpty(key)) {
+            return new ArrayList<>();
+        }
         RMapCache<String, List<String>> convertedList = redissonClient.getMapCache(FILE_PREVIEW_IMGS_KEY);
         return convertedList.get(key);
     }

为什么这样改

CacheServiceJDKImpl#getImgCache(String) 已经有完全相同的判空并返回空集合,
PictureFilePreviewImpl 也用 CollectionUtils.isEmpty(...) 兜底,
所以返回空集合是既有语义;本次只是把 redis 实现补齐到与 jdk 实现一致。

没有新增依赖(commons-lang3 已在依赖树中),没有改接口,没有改其他实现。

验证

  1. 本地 A/B 对照(真 Redis + Redisson 4.0.0,直接调用 new CacheServiceRedisImpl(redissonClient).getImgCache(null))
    • 修复前:复现出与线上逐行一致的异常栈(RedissonMap 448/1215/1211/588)
    • 修复后:返回 [];getImgCache("") 返回 [];正常 key 与 putImgCache 读写回环行为不变
  2. 真实环境双节点验证(Kylin V10 SP3 aarch64 / kkFileView 5.0.2 / cache.type=redis)
    • 修复前:图片预览 HTTP 500,进程日志 NPE 累计 10+ 次
    • 修复后:同一请求 HTTP 200 并正常返回图片预览页,新进程 NPE 0 次
    • 经业务入口(前置负载均衡反代)连续请求 6 次(覆盖两个节点)全部 200
    • 启动日志无异常:kkFileView 服务启动完成,耗时:6.06s / Tomcat started on port 8012

未改动的同类风险点(说明理由)

CacheServiceRedisImpl 的 getPDFCache(String)、putImgCache(String, List) 同样没有判空,
但核对现有全部调用点后确认实参不会是 null:

  • getPDFCache(String) ← FileHandlerService#getConvertedFile(key) ← 实参是 fileAttribute.getName()
  • putImgCache ← 实参是字符串拼接结果(fileName + packagePath / cacheName)

因此本次只做最小改动修复已确认的崩溃路径;如需要,可以另外统一加固。

CacheServiceRedisImpl#getImgCache(String) calls RMapCache#get with a null key, which makes Redisson 4.x throw NullPointerException ("map key can't be null").

compressFileKey comes from the optional kkCompressfileKey URL parameter that only archive batch preview carries, so it is null for a plain image URL. With cache.type=redis this makes every image preview fail with HTTP 500.

CacheServiceJDKImpl#getImgCache already guards this case and returns an empty list; align the redis implementation with the same semantics.

@klboke klboke left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已核对当前提交 2069d121 的调用链和改动,修复方向正确。普通图片预览不会设置 compressFileKey,Redis 实现此前将 null 直接传给 Redisson RMapCache#get,会抛出异常;提前返回空列表与 JDK 缓存实现及 PictureFilePreviewImpl 的现有处理一致,非空 key 的读取路径不变。

我在该提交上运行了 mvn -q -pl server -DskipTests compile,结果通过。可以合并。建议后续补一条覆盖 null 和空字符串 key 的回归测试,避免同类问题再次出现;这条建议不阻碍本次修复。

@klboke

klboke commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

代码有冲突了,么法直接合并了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] cache.type=redis 时图片预览必然 500(Redisson: map key can't be null)

2 participants