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

问题描述

我需要通过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存档。


不幸的是,有一个bug in the library。要解决此问题,您必须像这样实现包装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 count)
    {
        _stream.Write(buffer,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))
{
    ...
}