Remote file access (3/4): downloadFile(remote, local) — the symmetric, digest-verified counterpart of uploadFile
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 5/5
- 预计耗时
- 一周以上
- 新手友好度
- 35/100
- Issue 类型
- 功能
- 描述清晰度
- 基本清楚
- 活跃度
- 冷清
- 技术栈
- java
- 领域
- api, backend, networking
调研方向
先阅读 WinRMClient.uploadFile 以及 issue #146 中的范围/流式读取,然后检查 ShellFileCopy.digestHex、parseAnyDigest、CERTUTIL_ALGORITHMS 和配额重试常量。使用 FakeWsmanServer 的测试覆盖往返、摘要不匹配、中断和相同目标,并在 WinRMLiveTest 中添加实时检查。完成标准是 mvn verify site 通过,file-transfers.md 和 README 记录这些保证以及 SMB 建议,并且不残留部分目标文件。
由索引模型根据 Issue 内容生成。
描述
Third issue of the remote file access family: the missing symmetric half of
WinRMClient.uploadFile(Path, String). Builds onopenStream()from #146 (the foundation) — it does not need the listing of #145.
Context
client.uploadFile(Path localFile, String remoteFile) has existed since the smbj removal (#117): it pushes a local file through the WinRM channel itself, digest-verified, skipping the transfer when the destination already has identical content. There is no way back. Every caller that needs to retrieve a remote log or a command's output file has to shell out to type/Get-Content and hope the encoding survives.
downloadFile is the mirror image, and it should mirror the upload's guarantees, not just its direction: verified integrity, no wasted transfer, and no half-written local file left behind on failure.
Proposed API
Symmetric with the existing method, on the client:
client.downloadFile("C:\\Windows\\Temp\\collect.log", Path.of("collect.log"));
And as a terminal on the per-path request, for the fluent form:
long bytes = client.file("C:\\Windows\\Temp\\collect.log")
.downloadTo(Path.of("collect.log"));
Flow — one remote digest probe
- Probe the remote digest with
ShellFileCopy.digestProbe/parseAnyDigest. - If the local destination exists with the same digest → no transfer, return.
- Otherwise
client.file(remote).openStream()→DigestInputStream→ temporary file in the destination's directory. - Compare the received digest with the probed one; on match
fsyncandATOMIC_MOVEonto the target. On any failure (mismatch, timeout, interruption) delete the temporary file.
A file modified between the probe and the transfer shows up as a digest mismatch — the correct outcome.
Requirements
- Digest verification, both ways. Compute the remote digest with the probe
ShellFileCopyalready has (certutil -hashfile, SHA256 with a SHA1 fallback — seeCERTUTIL_ALGORITHMS), compare it with the digest of the received bytes, and fail with the same shape ofWindowsRemoteExceptionthe upload path raises on a mismatch. ReuseShellFileCopy.digestHex/parseAnyDigestrather than reimplementing. - Skip an identical transfer, matching the upload's behavior: if the local destination already exists with the same digest, do not transfer, and say so (return value or documented no-op).
- Atomic destination: stream into a temporary file in the destination's directory,
fsync, thenATOMIC_MOVEonto the target. A failure or a timeout must never leave a truncated file at the destination path. - Bounded memory: built on the streaming read, never
readBytes()into abyte[]first. A 500 MB file must download in constant memory (slowly — see below). - Downloads are not resumable — decided, and deliberately out of scope: an interrupted download starts over. Resuming would mean tracking verified byte counts across attempts and re-validating that the remote file has not changed, which is more state machine than this transport's speed justifies. Say so in the Javadoc and in
file-transfers.mdso callers do not expect otherwise. - Reuse the quota-rejection retry (
isRetryableQuotaRejection,QUOTA_RETRIES,QUOTA_RETRY_DELAY_MILLIS): a long download hits the same WinRM operation quotas the upload does. - Directory destination:
downloadFile(remote, Path.of("C:\\dir"))where the local path is an existing directory writesdir\<remote file name>, likecp— decided (the CLIgetdefault destination in #148 needs the same resolution). Document it. - Honest performance documentation. Base64 through a command shell is roughly an order of magnitude slower than SMB; publish a measured figure from the live host in
file-transfers.mdand keep the existing "not a bulk transport" caveat prominent. Callers moving gigabytes should use SMB, and the docs should say so. - Timeout is the client's wall-clock deadline for the blocking method; a large download will need an explicitly raised timeout, and the exception message must make the cause obvious (bytes transferred / total when it fired).
Tests & docs
- Round-trip test:
uploadFilethendownloadFilereturns byte-identical content, for a binary file with every byte value and for a file with a non-ASCII name. FakeWsmanServertests: digest mismatch → failure with no file at the destination; interruption mid-transfer → no partial file left behind and no half-written temporary file; identical-digest destination → no transfer performed.WinRMLiveTest: download a real file fromanaxagoreand verify its digest.file-transfers.mdextended with the download direction, the integrity and atomicity guarantees, the measured throughput, and the SMB recommendation for bulk data; README snippet next touploadFile.
Acceptance criteria
downloadFileretrieves byte-exact content, digest-verified against the remote host.- No partial file is ever visible at the destination path — verified by a test that interrupts mid-transfer.
- An identical local file is not re-downloaded.
- A file substantially larger than the JVM heap downloads successfully.
mvn verify sitegreen: no checkstyle/PMD/SpotBugs findings, full Javadoc, docs updated.
🤖 Generated with Claude Code
- 主要语言
- Java
- 星标
- 13
- 派生
- 4
- 平均合并
- 1 天 5 小时
- 30 天内合并 PR
- 18
环境准备
这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
MetricsHub/winrm-java 的其他 Issue
-
enhancement
难度 5/5 一周以上 新手友好度 30/100
MetricsHub/winrm-java#194 ·
维护者通常 1 天内回复
-
难度 5/5 一周以上 新手友好度 38/100
MetricsHub/winrm-java#176 ·
维护者通常 1 天内回复
查看 MetricsHub/winrm-java 的全部 Issue
相似的 Issue
-
bug
难度 2/5 1-3 小时 新手友好度 75/100
apache/skywalking#14120 ·
维护者通常 1 天内回复
-
enhancement
难度 2/5 1-3 小时 新手友好度 75/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 85/100
micronaut-projects/micronaut-core#13677 ·
维护者通常 1 天内回复
-
new feature
难度 2/5 1-3 小时 新手友好度 62/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 78/100
apache/rocketmq-dashboard#5594 ·
维护者通常 3 天内回复