在 C# 中列出位于 SFTP 服务器上的 ZIP 文件中的文件

List files inside ZIP file located on SFTP server in C#

我需要通过 ASP.NET Core 以编程方式处理来自 SFTP 服务器 (WinSCP) 的 ZIP 文件中的文件夹。

有什么方法可以在不下载到本地计算机的情况下获取 ZIP 文件中的文件列表?作为 文件大小会很大并且不会以一致的方式。任何帮助将不胜感激。

使用 SSH.NET library,它可以像这样简单:

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 包裹起来:

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