Remote file access (3/4): downloadFile(remote, local) — the symmetric, digest-verified counterpart of uploadFile
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- java
- Domain
- api, backend, networking
Research direction
Start by reading WinRMClient.uploadFile and the ranged/streaming read from issue #146, then inspect ShellFileCopy.digestHex, parseAnyDigest, CERTUTIL_ALGORITHMS, and the quota-retry constants. Use the FakeWsmanServer tests to cover round trips, digest mismatches, interruptions, and identical destinations, and add the live check in WinRMLiveTest. Done means mvn verify site passes, file-transfers.md and the README document the guarantees and SMB recommendation, and no partial destination file remains.
Written by the indexing model from the issue text.
Description
Third issue of the remote file access family: the missing symmetric half of
WinRMClient.uploadFile(Path, String). Builds on the ranged/streaming read of #146.
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"));
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 should writedir\<remote file name>— or reject it. Pick one, 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
- Dominant language
- Java
- Stars
- 11
- Forks
- 4
- Avg merge
- 5d 5h
- Merged PRs (30d)
- 6
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from MetricsHub/winrm-java
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
MetricsHub/winrm-java#148 ·
-
Difficulty 5/5 Over a week Newbie friendliness 38/100
MetricsHub/winrm-java#146 ·
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
MetricsHub/winrm-java#145 ·
-
Kerberos credential delegation: allowDelegation() and CLI --allow-delegate (winrs -allowdelegate) Open
Difficulty 4/5 3-5 days Newbie friendliness 48/100
MetricsHub/winrm-java#141 ·
-
Difficulty 4/5 3-5 days Newbie friendliness 68/100
MetricsHub/winrm-java#139 ·
All issues in MetricsHub/winrm-java
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
elastic/gradle-plugins#157 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
cryptomator/hub#497 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
johanhaleby/occurrent#1120 ·