With SSH.NET 库,它可能很简单:
using (var client = new SftpClient(host, username, password)
{
client.Connect();
using (Stream stream = client.OpenRead("/remote/path/archive.zip"))
using (var archive = new ZipArchive(stream, ZipArchiveMode.Read))
{
foreach (var entry in archive.Entries)
{
Console.WriteLine(entry);
}
}
}
你需要参考一下System.IO.Compression
装配得到ZipArchive.
该代码只会读取(下载)ZIP 中央目录记录,而不是整个 ZIP 存档。证明请参见答案末尾。
不幸的是,有一个库中的错误。要解决这个问题,您必须实现一个包装器Stream
像这样的实现:
class FixStream : Stream
{
public override long Seek(long offset, SeekOrigin origin)
{
long result;
// workaround for SSH.NET bug in implementation of SeekOrigin.End
if (origin == SeekOrigin.End)
{
result = _stream.Seek(Length + offset, SeekOrigin.Begin);
}
else
{
result = _stream.Seek(offset, origin);
}
return result;
}
// passthrough implementation of the rest of Stream interface
public override bool CanRead => _stream.CanRead;
public override bool CanSeek => _stream.CanSeek;
public override bool CanWrite => _stream.CanWrite;
public override long Length => _stream.Length;
public override long Position {
get => _stream.Position; set => _stream.Position = value; }
public FixStream(Stream stream)
{
_stream = stream;
}
public override void Flush()
{
_stream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return _stream.Read(buffer, offset, count);
}
public override void SetLength(long value)
{
_stream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
_stream.Write(buffer, offset, count);
}
private Stream _stream;
}
并包上SftpFileStream
to it:
using (Stream stream = client.OpenRead("/remote/path/archive.zip"))
using (var stream2 = new FixStream(stream))
using (var archive = new ZipArchive(stream2, ZipArchiveMode.Read))
{
...
}
作为它确实有效的证明,我已将日志记录添加到所有方法中FixStream
。当使用具有两个条目的 18 MB(18265315 字节)ZIP 存档的代码时,会生成以下内容。因此仅从流中读取了 244 个字节。实际上,更多内容是从实际的远程 SFTP 文件中读取的,因为 SSH.NET 缓冲了读取(否则代码将非常无效,特别是在这种情况下,正如您所看到的那样)ZipArchive
进行大量小读)。默认 SSH.NET 缓冲区为 32 KB (SftpClient.BufferSize
).
Tried to seek to -18 from End => converting to seek to 18265297 from Begin
Seeked to 18265297 from Begin => 18265297
Seeked to -32 from Current => 18265265
Tried to read 32, got 32
Seeked to -32 from Current => 18265265
Seeked to 28 from Current => 18265293
Tried to read 4, got 4
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 4, got 4
Tried to read 4, got 4
Tried to read 2, got 2
Seeked to 18265075 from Begin => 18265075
Tried to read 4, got 4
Tried to read 1, got 1
Tried to read 1, got 1
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 4, got 4
Tried to read 4, got 4
Tried to read 4, got 4
Tried to read 4, got 4
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 4, got 4
Tried to read 4, got 4
Tried to read 28, got 28
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 32, got 32
Set position to 18265185
Tried to read 4, got 4
Tried to read 1, got 1
Tried to read 1, got 1
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 4, got 4
Tried to read 4, got 4
Tried to read 4, got 4
Tried to read 4, got 4
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 4, got 4
Tried to read 4, got 4
Tried to read 26, got 26
Tried to read 2, got 2
Tried to read 2, got 2
Tried to read 32, got 32
Set position to 18265293
Tried to read 4, got 4