Repository navigation
Conversation
…ngthen key to 12 hex
…ngthen key to 12 hex
…ngthen key to 12 hex
klboke
left a comment
There was a problem hiding this comment.
Changes are needed before merging this head (ae73789107f8360c5077b5ff5743c3e4f1d9bc66).
-
Archive-contained previews now point at nonexistent source files. In
FileHandlerService#getFileAttribute, theisCompressFilebranch obtains the already-extracted relative path fromkkCompressfilepathand setsskipDownLoad=true. The new unconditionalphysicalName = fileKey + "_" + originFileNamethen changes that existing path.DownloadUtils.downLoaddoes not download or rename archive entries; it returnsfileDir + fileAttribute.getName(). I reproduced this using the actual handler with an existingarchive.zip_/docs/report.pdf: the extracted file exists, butgetOriginFilePath()points toe1b8d850b7abea186f9f58a6_archive.zip_/docs/report.pdf, which does not exist. PDF/Office/text/conversion paths inside the archive consequently fail. Namespace the downloaded outer archive and its generated outputs while preserving the actual extracted entry path, and add a handler/preview regression using an existing extracted file. -
The new regression suite fails.
mvn -q -pl server -Dtest=KkFileUtilsUrlCacheKeyTests testproducesTests run: 4, Failures: 1:keyIsSafeForFileNameexpects 12 characters but receives 24.urlCacheKeycurrently formats 12 digest bytes as two hex characters each. Make the intended hash length, implementation, documentation, and assertions agree.
The current CI packaging jobs skip executing Java tests, so a green packaging build cannot clear these failures.
…ition kekingcn#796 的缓存隔离把 FileAttribute.name 改成了物理唯一名(<hash>_原名), 该物理名被 pdfjs 等前端当作下载保存名,导致用户拿到的文件名带 hash 前缀(UX 回归)。 在 /getCorsFile 流式响应上加 Content-Disposition 用源 URL 的原始文件名, 预览渲染不受影响(pdfjs 通过 XHR 取字节,Content-Disposition 仅作用于下载动作)。 getName() 仍保持物理唯一名,kekingcn#791 的同名文件碰撞隔离不受影响。
|
补充一个下载文件名 UX 修复(commit #796 的缓存隔离把 修复:在
注:本地文件( |
klboke
left a comment
There was a problem hiding this comment.
Re-reviewed the updated head be0e5a1addd03238786b3bd4c96fb279db2011c9. The new commit adds download-name handling, but both blockers from the previous review remain reproducible, and the new header handling also needs correction. Details are attached to the affected lines.
Validation on this exact head:
mvn -q -pl server clean -Dtest=KkFileUtilsUrlCacheKeyTests test: 4 tests, 1 failure (expected: <12> but was: <24>).- Actual
FileHandlerService#getFileAttributewith an existing extracted PDF: the original extracted file exists; the newly computed hash-prefixed source path does not. - Actual
OnlinePreviewController#getCorsFileagainst a local HTTP fixture: all four requests return 200 and file bytes, but query-bearing URLs produce incorrectContent-Dispositionfilenames. The bundled PDF.js header parser rejects thereport.pdf?token=demoandpartBfilenames.
The latest three-platform builds and 27 E2E checks are green. Packaging skips Java tests, and the archive smoke tests inspect the directory page without opening an extracted entry, so those checks do not cover the confirmed failures above.
| boolean isHtmlView = suffix.equalsIgnoreCase("xls") || suffix.equalsIgnoreCase("xlsx") || suffix.equalsIgnoreCase("csv") || suffix.equalsIgnoreCase("xlsm") || suffix.equalsIgnoreCase("xlt") || suffix.equalsIgnoreCase("xltm") || suffix.equalsIgnoreCase("et") || suffix.equalsIgnoreCase("ett") || suffix.equalsIgnoreCase("xlam"); | ||
| // #791: 用完整源 URL 的短哈希为物理落地名加前缀,避免不同路径下同名文件互相覆盖(内容污染) | ||
| String fileKey = KkFileUtils.urlCacheKey(url); | ||
| String physicalName = fileKey + "_" + originFileName; // 物理唯一名;getName 同步设为 physicalName,使下载/转换/引用三处路径一致 |
There was a problem hiding this comment.
[P1] Preserve the actual extracted path for archive entries.
This blocker from the previous review is unchanged. For an archive-contained file, originFileName has already been set to the existing extracted relative path from kkCompressfilepath, and skipDownLoad=true. Adding another URL hash here changes getName() and getOriginFilePath() to a path that was never extracted. On this head, an existing archive.zip_/docs/report.pdf becomes e1b8d850b7abea186f9f58a6_archive.zip_/docs/report.pdf (does not exist). DownloadUtils.downLoad returns this path without downloading or renaming archive entries, so nested previews fail.
Namespace the outer archive and its outputs while preserving the actual extracted entry path. Add a regression that opens an already-extracted file through the real handler/preview flow; the current archive directory smoke test cannot detect this.
| MessageDigest md = MessageDigest.getInstance("MD5"); | ||
| byte[] digest = md.digest(url.getBytes(StandardCharsets.UTF_8)); | ||
| StringBuilder sb = new StringBuilder(24); | ||
| for (int i = 0; i < 12 && i < digest.length; i++) { |
There was a problem hiding this comment.
[P2] Make the hash length agree with the documented contract and regression test.
This still emits 24 hexadecimal characters: 12 digest bytes times two characters per byte. Both the Javadoc and KkFileUtilsUrlCacheKeyTests.keyIsSafeForFileName require 12 characters. Running the new suite on this exact head still yields 4 tests with 1 failure (expected: <12> but was: <24>). Choose the intended length and align the implementation, documentation, and assertions before merging.
| String originalFileName = urlPath; | ||
| int lastSlash = originalFileName.lastIndexOf('/'); | ||
| if (lastSlash >= 0 && lastSlash + 1 < originalFileName.length()) { | ||
| originalFileName = originalFileName.substring(lastSlash + 1); | ||
| } | ||
| response.setHeader("Content-Disposition", "attachment; filename=\"" + originalFileName + "\""); |
There was a problem hiding this comment.
[P2] Derive the download name from the URL path, not the complete URL string.
The new code includes query parameters in the filename and also treats slashes inside query values as path separators. Calling the actual controller against an HTTP fixture produces:
/report.pdf?token=demo->filename="report.pdf?token=demo";/report.pdf?token=partA/partB->filename="partB";/download?id=42&fullfilename=report.pdf->filename="download?id=42&fullfilename=report.pdf".
The bundled PDF.js extractFilenameFromHeader rejects the first two values because they do not end in .pdf, so this does not restore the intended download name for those ordinary URL forms. Parse the URL path separately, honor the supported fullfilename override, and serialize a properly escaped/encoded Content-Disposition filename. Please cover query parameters, a slash inside a query value, and the filename override with controller-level tests.
关联 Issue
Closes #791
审查后修正(post-review fix)
代码审查中发现一处 P0 阻断性回归,已修复并推送到本分支:
originFilePath/outFilePath/getCacheName())改成physicalName,却保留attribute.setName(originFileName)(basename)。而 kkFileView 里getName()既当展示名、又当DownloadUtils.downLoad(fileAttribute, fileName)的下载落地文件名、还当转换缓存 key——所有*FilePreviewImpl(Pdf/Office/Common/SimText/Cad/Media/Tiff 共 7 处)都是fileName = fileAttribute.getName()。于是下载把文件存成test.docx,前端/转换却去读fileKey_test.docx→ 找不到 → 预览全面 404。setName(originFileName)改为setName(physicalName),使getName()与物理路径统一带fileKey。下载 / 转换 / 引用三处路径一致,不再失配。urlCacheKey的哈希长度从 8 位提升到 12 位 hex,降低大规模下的生日碰撞概率;测试补一条physicalNameDiffersAcrossUrlsWithSameBasename断言防护此回归。问题根因
转换产物在
file.dir下只按源文件 basename 命名,不包含源路径、也不含完整 URL 的哈希:FileHandlerService.getFileAttribute()通过WebUtils.getFileNameFromURL(url)取 URL 最后一段作为originFileName,再派生cacheFileName/outFilePath/originFilePath(FileHandlerService.java约 L274–288)。getCacheFileName()对 PDF / COMPRESS / 图片 / 文本等类型直接返回originFileName,OFFICE 返回cacheFilePrefixName + pdf。因此两个不同 URL 但同名文件(如
.../pathA/test.docx与.../pathB/test.docx)会写同一物理路径,后预览的覆盖先预览的转换结果,把 A 用户的文档内容吐给 B 用户(多后端 / 多租户 / AList 等场景下的内容污染)。修复方案
在两处做最小改动,把物理落地名按「完整源 URL 的短哈希」命名空间隔离,同时保持前端展示名不变:
KkFileUtils新增纯静态工具urlCacheKey(url):取完整 URL 的 MD5 前 8 位十六进制,结果不含/、\、..等路径分隔符,可直接拼入文件名。FileHandlerService.getFileAttribute()中:String fileKey = KkFileUtils.urlCacheKey(url);physicalName = fileKey + "_" + originFileName;cacheFilePrefixName/cacheFileName(传给getCacheFileName)/originFilePath全部基于physicalName派生;attribute.setName(physicalName):展示名同步改为physicalName(即fileKey_原始名)。这样同一 URL 始终映射到同一物理名(缓存可命中),不同路径同名文件各自隔离,互不覆盖。
回归测试
新增
server/src/test/java/cn/keking/utils/KkFileUtilsUrlCacheKeyTests.java:[0-9a-f]、无路径分隔符(可作文件名)。兼容性说明
file.dir下的物理文件名与 PDF 缓存 map 的 key,不改变任何对外接口签名,各*FilePreviewImpl消费getOutFilePath()/getCacheName()等已生成字段,无需改动。复现 / 验证步骤(本地)
http://host/pathA/test.docx(内容 AAAA)与http://host/pathB/test.docx(内容 BBBB)。file.dir下生成xxxxxxxx_pathA/test.docx与xxxxxxxx_pathB/test.docx两个独立文件。