使用SSH.NET库从SFTP下载文件

54
string host = @"ftphost";
string username = "user";
string password = "********";
string localFileName  = System.IO.Path.GetFileName(@"localfilename");
string remoteDirectory = "/export/";
using (var sftp = new SftpClient(host, username, password))
{
    sftp.Connect();
    var files = sftp.ListDirectory(remoteDirectory);
    foreach (var file in files)
    {
        if (!file.Name.StartsWith("."))
        {
            string remoteFileName = file.Name;
            if (file.LastWriteTime.Date == DateTime.Today)

            Console.WriteLine(file.FullName);

            File.OpenWrite(localFileName);

            string sDir = @"localpath";

            Stream file1 = File.OpenRead(remoteDirectory + file.Name);
            sftp.DownloadFile(remoteDirectory, file1);
        }
    }
}
我正在使用SSH.NET(Renci.SshNet)库来处理SFTP服务器。我需要做的是根据今天的日期从SFTP服务器上抓取特定文件夹中的文件,然后将这些文件从SFTP服务器复制到我的本地驱动器或服务器。
以上是我拥有的代码,但它不起作用。有时它会说文件不存在,但有时我将下载的文件不会存在于我的本地服务器上,但我需要下载那一天上传到远程文件夹中的任何文件。
有人能看一眼并找出问题吗?我相信它与流的部分有关。除了上传文件外,我没有太多使用FTP的经验。我使用的代码基于此示例
6个回答

61

使用SSH.NET库下载文件的简单工作代码如下:

using (Stream fileStream = File.Create(@"C:\target\local\path\file.zip"))
{
    sftp.DownloadFile("/source/remote/path/file.zip", fileStream);
}

另请参见使用SSH.NET SFTP在C#中下载目录


解释一下,为什么您的代码不起作用:

SftpClient.DownloadFile的第二个参数是一个流,用于写入已下载内容。

您正在传递读取流而不是写入流。而且,您使用的是打开远程路径的读取流,这与File类只能处理本地文件的情况不符。

只需放弃File.OpenRead行,并改为使用先前的File.OpenWrite调用的结果(现在根本未使用):

Stream file1 = File.OpenWrite(localFileName);

sftp.DownloadFile(file.FullName, file1);

或者更好的方法是使用File.Create来丢弃本地文件可能具有的任何先前内容。

我不确定你的localFileName是否应该包含完整路径还是只有文件名。如果需要的话,您可能还需要添加路径(将localFileNamesDir组合?)


30

虽然这个示例可以工作,但不是处理流的正确方式...

您需要使用using语句确保关闭文件/流。 此外,添加try/catch以处理IO错误...

       public void DownloadAll()
    {
        string host = @"sftp.domain.com";
        string username = "myusername";
        string password = "mypassword";
        
        string remoteDirectory = "/RemotePath/";
        string localDirectory = @"C:\LocalDriveFolder\Downloaded\";

        using (var sftp = new SftpClient(host, username, password))
        {
            sftp.Connect();
            var files = sftp.ListDirectory(remoteDirectory);
            
            foreach (var file in files)
            {
                string remoteFileName = file.Name;
                if ((!file.Name.StartsWith(".")) && ((file.LastWriteTime.Date == DateTime.Today))

                    using (Stream file1 = File.Create(localDirectory + remoteFileName))
                    { 
                        sftp.DownloadFile(remoteDirectory + remoteFileName, file1);
                    }
            }

        }
    }

5

这是我修改过的@Merak Marey的代码。我正在检查文件是否已存在,以及不同的下载目录用于.txt和其他文件。

        static void DownloadAll()
    {
        string host = "xxx.xxx.xxx.xxx";
        string username = "@@@";
        string password = "123";string remoteDirectory = "/IN/";
        string finalDir = "";
        string localDirectory = @"C:\filesDN\";
        string localDirectoryZip = @"C:\filesDN\ZIP\";
        using (var sftp = new SftpClient(host, username, password))
        {
            Console.WriteLine("Connecting to " + host + " as " + username);
            sftp.Connect();
            Console.WriteLine("Connected!");
            var files = sftp.ListDirectory(remoteDirectory);

            foreach (var file in files)
            {

                string remoteFileName = file.Name;

                if ((!file.Name.StartsWith(".")) && ((file.LastWriteTime.Date == DateTime.Today)))
                { 

                    if (!file.Name.Contains(".TXT"))
                    {
                        finalDir = localDirectoryZip;
                    } 
                    else 
                    {
                        finalDir = localDirectory;
                    }


                    if (File.Exists(finalDir  + file.Name))
                    {
                        Console.WriteLine("File " + file.Name + " Exists");
                    }else{
                        Console.WriteLine("Downloading file: " + file.Name);
                          using (Stream file1 = File.OpenWrite(finalDir + remoteFileName))
                    {
                        sftp.DownloadFile(remoteDirectory + remoteFileName, file1);
                    }
                    }
                }
            }



            Console.ReadLine();

        }

1
如果你想基于扩展名添加一些东西,可以使用FileInfo fi,你可以简单地这样做:string ext = fi.Extension;和一个switch。因为LOVE-LETTER-FOR-YOU.TXT.vbs将会是一个误报,或者使用Path.GetExtension(myFilePath); - Drag and Drop
注意,您正在检查文件名是否包含“.TXT”,并且根据服务器操作系统,您可能会得到“.txt”(小写)。最好将remoteFilename变量转换为小写,并在条件中使用它,而不是使用file.Name。 - Merak Marey
更好的写法:if (!file.Name.ToLower().EndsWith(".txt")) - Gordon Bell

2

Massimiliano的解决方案有一个问题,会导致文件没有完全下载。FileStream必须关闭。这对于加密文件特别是一个问题。如果不关闭流,它们将间歇性地无法完全解密。

var files = sftp.ListDirectory(remoteVendorDirectory).Where(f => !f.IsDirectory);

foreach (var file in files)
{
    var filename = $"{LocalDirectory}/{file.Name}";
    if (!File.Exists(filename))
    {
        Console.WriteLine("Downloading  " + file.FullName);
        using (var localFile = File.OpenWrite(filename))
            sftp.DownloadFile(file.FullName, localFile);
    }
}

1
这解决了我的问题。
var files = sftp.ListDirectory(remoteVendorDirectory).Where(f => !f.IsDirectory);

foreach (var file in files)
{
    var filename = $"{LocalDirectory}/{file.Name}";
    if (!File.Exists(filename))
    {
        Console.WriteLine("Downloading  " + file.FullName);
        using (var localFile = File.OpenWrite(filename))
              sftp.DownloadFile(file.FullName, localFile);
    }
}

0

没有提供具体错误信息,很难给出具体的建议。

然而,我使用相同的示例时,在使用 localFileName 变量时在 File.OpenWrite 上得到了权限异常,因为使用 Path.GetFile 指向的位置明显没有打开文件的权限 > C:\ProgramFiles\IIS(Express)\filename.doc

我发现使用 System.IO.Path.GetFileName 是不正确的,应改用 System.IO.Path.GetFullPath,指向以 "C:\..." 开头的文件即可。

此外,在 FileExplorer 中打开解决方案并为 asp.net 授予文件或任何持有该文件的文件夹的权限。这样我就能下载我的文件了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接