Class RemoteFile

java.lang.Object
org.metricshub.winrm.RemoteFile

public final class RemoteFile extends Object
A file or directory on the remote host, obtained with WinRMClient.file(String): read a file's content — whole, as a byte range, or as a stream — download it to a local file, or compute its digest, get its properties (info(), exists()), or list() a directory. Nothing is sent until a terminal (readBytes(), readText(Charset), openStream(), openReader(Charset), downloadTo(Path), digest(String), info(), exists()) is called.

 byte[] content = client.file("C:\\Windows\\Temp\\collect.bin").readBytes();

 // The last 8 KiB of a log: one seek, no scan
 String tail = client.file("D:\\logs\\huge.log").offset(-8192).readText(StandardCharsets.UTF_8);

 try (BufferedReader reader = client.file("D:\\logs\\huge.log").openReader(StandardCharsets.UTF_8)) {
 	reader.lines().filter(l -> l.contains("ERROR")).forEach(System.out::println);
 }
 

The content travels through the WinRM connection itself (no SMB, no extra port): a small PowerShell script on the host writes the file's raw bytes to its standard output stream, past any text conversion, so every byte arrives as stored whatever the remote console code page. It is designed for configuration files, logs and small data files — not a bulk transport: the WinRM service forwards a command's output at about 2 MB/s, far slower than SMB. The host needs PowerShell (2.0 or later) in FullLanguage mode.

The file is opened with a share mode that tolerates other writers, so a log being written by a running service can be read; a file held with an exclusive lock (e.g. pagefile.sys) fails with a sharing violation. A read writes nothing on the host: its script travels on the command line, which limits the path to about 1,500 characters (fewer with non-Latin characters) — a longer path fails before anything is sent. Ranges are byte ranges, and reads are not snapshots: a file that grows or shrinks between two reads is read as it is at each read.

A request is not thread-safe; configure it and call its terminals from one thread.

  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final long
    Default cap of readBytes() and readText(Charset): 64 MiB.
  • Method Summary

    Modifier and Type
    Method
    Description
    digest(String algorithm)
    Compute the digest of the whole file on the host — nothing but the digest is transferred.
    long
    downloadTo(Path localFile)
    Download the whole file to a local file — the counterpart of WinRMClient.uploadFile(Path, String), with the same guarantees: verified: the host first reports the size and the SHA-256 digest of the file, and the received bytes must match both, so the local file is exactly one version of the remote file, never a mix of two: a change that reaches bytes not transferred yet fails the download; no wasted transfer: when the local file already has that digest, nothing is transferred; atomic: the content goes to a temporary file next to the destination (<name>.<random>.part), which is flushed to disk, then moved onto the destination in one step.
    boolean
    Tell whether the file or directory exists: info().isPresent().
    Get the properties of the file or directory: size, timestamps, attributes.
    length(long length)
    Read at most the given number of bytes (from the offset(long), once resolved) instead of reading to the end of the file.
    Prepare the listing of this path, a directory: set its filters, then call RemoteDirectoryListing.execute() or RemoteDirectoryListing.stream().
    maxBytes(long maxBytes)
    Set the size cap of readBytes() and readText(Charset), which hold the whole content in memory: reading more than the cap fails instead of risking an OutOfMemoryError.
    offset(long offset)
    Start reading at the given byte position instead of the beginning of the file.
    Open the content (the whole file, or the configured range) as a character stream decoded with the given charset: openStream() behind an InputStreamReader, with the same lifecycle — close it.
    Open the content (the whole file, or the configured range) as a stream, passed on as it arrives: memory is bounded by one protocol response, not by the file, and there is no size cap.
    Get the path of the file on the remote host.
    byte[]
    Read the content (the whole file, or the configured range) into memory, byte-exact: nothing is converted, and a byte order mark is kept.
    readText(Charset charset)
    Read the content (the whole file, or the configured range) into memory and decode it with the given charset — no guessing, no default.
    timeout(Duration timeout)
    Override the client's timeout for this request.

    Methods inherited from class java.lang.Object

    equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

  • Method Details

    • path

      public String path()
      Get the path of the file on the remote host.
      Returns:
      the path, as given to WinRMClient.file(String)
    • offset

      public RemoteFile offset(long offset)
      Start reading at the given byte position instead of the beginning of the file. A negative offset counts from the end: offset(-n) starts at max(0, size - n), the size being read by the same remote invocation that seeks (so a growing log is tailed from its current end); a file shorter than n is read whole. An offset past the end of the file reads nothing — it is not an error. Applies to every read terminal, not to downloadTo(Path) or digest(String).
      Parameters:
      offset - the byte position; negative counts from the end of the file
      Returns:
      this request
    • length

      public RemoteFile length(long length)
      Read at most the given number of bytes (from the offset(long), once resolved) instead of reading to the end of the file. A length beyond the end of the file reads what exists; a zero length reads nothing. Applies to every read terminal, not to downloadTo(Path) or digest(String).
      Parameters:
      length - how many bytes to read at most (zero or more)
      Returns:
      this request
      Throws:
      IllegalArgumentException - when the length is negative
    • maxBytes

      public RemoteFile maxBytes(long maxBytes)
      Set the size cap of readBytes() and readText(Charset), which hold the whole content in memory: reading more than the cap fails instead of risking an OutOfMemoryError. Defaults to DEFAULT_MAX_BYTES (64 MiB). The cap is also sent to the host, so no more than one byte past it is ever transferred. For larger content, use openStream(), which reads with bounded memory and has no cap.
      Parameters:
      maxBytes - the largest content accepted, in bytes
      Returns:
      this request
      Throws:
      IllegalArgumentException - when the cap is negative or exceeds the largest possible byte array
    • timeout

      public RemoteFile timeout(Duration timeout)
      Override the client's timeout for this request. For the blocking terminals (readBytes(), readText(Charset), downloadTo(Path), digest(String), info(), exists()) it is a wall-clock deadline for the whole operation; for openStream() and openReader(Charset) it is an inactivity timeout, the longest silence tolerated from the host between two chunks of content.
      Parameters:
      timeout - the timeout (at least one millisecond)
      Returns:
      this request
    • readBytes

      public byte[] readBytes()
      Read the content (the whole file, or the configured range) into memory, byte-exact: nothing is converted, and a byte order mark is kept.
      Returns:
      the content
      Throws:
      WinRMClientException - when the content exceeds maxBytes(long), or the file cannot be read (not found, a directory, access denied, sharing violation, PowerShell unavailable or constrained)
      WinRMTimeoutException - when the timeout elapses first
    • readText

      public String readText(Charset charset)
      Read the content (the whole file, or the configured range) into memory and decode it with the given charset — no guessing, no default. This method strips nothing: a byte order mark is handled exactly as the given charset's decoder handles it — UTF-8 keeps it (as U+FEFF), while UTF-16 consumes it to detect the byte order. A range boundary may split a multibyte character, which then decodes to U+FFFD at the edges, like any malformed input.
      Parameters:
      charset - the charset the file is encoded with
      Returns:
      the decoded content
      Throws:
      WinRMClientException - when the content exceeds maxBytes(long), or the file cannot be read
      WinRMTimeoutException - when the timeout elapses first
    • openStream

      public InputStream openStream()
      Open the content (the whole file, or the configured range) as a stream, passed on as it arrives: memory is bounded by one protocol response, not by the file, and there is no size cap. The file is opened before this method returns, so a file that cannot be read fails here; a failure midway is reported by read() (never as a silently short read).

      The stream must be closed — use try-with-resources. It holds the client's connection until it reaches its end or is closed; closing it early stops the remote read. Failures are reported through the unchecked WinRMClientException hierarchy, including from read(). The timeout is an inactivity timeout (see timeout(Duration)).

      Returns:
      the content stream, to use with try-with-resources
      Throws:
      WinRMClientException - when the file cannot be read
      WinRMTimeoutException - when the host does not answer in time
    • openReader

      public BufferedReader openReader(Charset charset)
      Open the content (the whole file, or the configured range) as a character stream decoded with the given charset: openStream() behind an InputStreamReader, with the same lifecycle — close it. Like readText(Charset), it strips nothing: a byte order mark is handled as the given charset's decoder handles it.
      Parameters:
      charset - the charset the file is encoded with
      Returns:
      the reader, to use with try-with-resources
      Throws:
      WinRMClientException - when the file cannot be read
      WinRMTimeoutException - when the host does not answer in time
    • downloadTo

      public long downloadTo(Path localFile)
      Download the whole file to a local file — the counterpart of WinRMClient.uploadFile(Path, String), with the same guarantees:
      • verified: the host first reports the size and the SHA-256 digest of the file, and the received bytes must match both, so the local file is exactly one version of the remote file, never a mix of two: a change that reaches bytes not transferred yet fails the download;
      • no wasted transfer: when the local file already has that digest, nothing is transferred;
      • atomic: the content goes to a temporary file next to the destination (<name>.<random>.part), which is flushed to disk, then moved onto the destination in one step. The destination is never seen truncated: a failure or a timeout leaves it as it was, and the temporary file is deleted as the transfer stops. A replaced file keeps its POSIX permissions; on Windows, the new file gets the permissions the directory gives new files.
      When localFile is an existing directory, the file is written into it under its remote name, like cp — so the remote name must not contain a colon (an alternate data stream, or a drive-relative path): name the local file explicitly for those. The destination's directory is created when needed. Memory is bounded whatever the size of the file. The offset(long), length(long) and maxBytes(long) settings do not apply.

      The timeout is a wall-clock deadline for the whole download, and the transfer runs at about 1.8 MB/s: a large file needs a raised timeout(Duration). A deadline that fires while the verified file is being moved onto the destination lets that move complete: the download then succeeds. Downloads are not resumable — one that fails or times out starts over.

      Parameters:
      localFile - the local file to write, or an existing directory to write the file into
      Returns:
      the number of bytes transferred: the size of the file, or 0 when the local file already had the same content
      Throws:
      WinRMClientException - when the file cannot be read, the integrity check fails, or the local file cannot be written
      WinRMTimeoutException - when the timeout elapses first; the message tells how much was transferred
      IllegalArgumentException - when localFile is a directory and the remote name contains a colon
    • digest

      public String digest(String algorithm)
      Compute the digest of the whole file on the host — nothing but the digest is transferred. The offset(long) and length(long) settings do not apply.
      Parameters:
      algorithm - MD5, SHA1, SHA256, SHA384 or SHA512
      Returns:
      the digest, as lowercase hexadecimal
      Throws:
      IllegalArgumentException - when the algorithm is not supported
      WinRMClientException - when the file cannot be read
      WinRMTimeoutException - when the timeout elapses first
    • info

      public Optional<RemoteFileInfo> info()
      Get the properties of the file or directory: size, timestamps, attributes. A path that does not exist is not an error: the result is empty. The offset(long) and length(long) settings do not apply.
      Returns:
      the properties, or empty when the path does not exist
      Throws:
      WinRMClientException - when the properties cannot be read (access denied, invalid path, PowerShell unavailable or constrained)
      WinRMTimeoutException - when the timeout elapses first
    • exists

      public boolean exists()
      Tell whether the file or directory exists: info().isPresent().
      Returns:
      true when the path exists
      Throws:
      WinRMClientException - when the path cannot be checked (e.g. access denied)
      WinRMTimeoutException - when the timeout elapses first
    • list

      public RemoteDirectoryListing list()
      Prepare the listing of this path, a directory: set its filters, then call RemoteDirectoryListing.execute() or RemoteDirectoryListing.stream(). The timeout of this request, when set, carries over.
      
       RemoteFileList logs = client.file("C:\\inetpub\\logs").list().glob("*.log").recursive().execute();
       
      Returns:
      the listing request