What is the best way to download all files in a remote directory using C# and FTP and save them to a local directory?
使用C#和FTP下载远程目录中的所有文件并将其保存到本地目录的最佳方法是什么?
Thanks.
5 个解决方案
#1
downloading all files in a specific folder seems to be an easy task. However, there are some issues which has to be solved. To name a few:
下载特定文件夹中的所有文件似乎是一项简单的任务。但是,有一些问题需要解决。仅举几例:
- How to get list of files (System.Net.FtpWebRequest gives you unparsed list and directory list format is not standardized in any RFC)
- What if remote directory has both files and subdirectories. Do we have to dive into the subdirs and download it's content?
- What if some of the remote files already exist on the local computer? Should they be overwritten? Skipped? Should we overwrite older files only?
- What if the local file is not writable? Should the whole transfer fail? Should we skip the file and continue to the next?
- How to handle files on a remote disk which are unreadable because we don’t have sufficient access rights?
- How are the symlinks, hard links and junction points handled? Links can easily be used to create an infinite recursive directory tree structure. Consider folder A with subfolder B which in fact is not the real folder but the *nix hard link pointing back to folder A. The naive approach will end in an application which never ends (at least if nobody manage to pull the plug).
如何获取文件列表(System.Net.FtpWebRequest为您提供未解析的列表和目录列表格式在任何RFC中都未标准化)
如果远程目录同时包含文件和子目录,该怎么办?我们是否必须深入了解子目录并下载它的内容?
如果本地计算机上已存在某些远程文件,该怎么办?他们应该被覆盖吗?跳过?我们应该只覆盖旧文件吗?
如果本地文件不可写怎么办?整个转移是否会失败?我们应该跳过文件继续下一个吗?
如何处理远程磁盘上由于我们没有足够的访问权限而无法读取的文件?
如何处理符号链接,硬链接和连接点?链接可以很容易地用于创建无限递归目录树结构。考虑带有子文件夹B的文件夹A,实际上它不是真正的文件夹,而是指向文件夹A的* nix硬链接。天真的方法将在一个永不停止的应用程序中结束(至少如果没有人设法拔掉插件)。
Decent third party FTP component should have a method for handling those issues. Following code uses our Rebex FTP for .NET.
体面的第三方FTP组件应该有一个处理这些问题的方法。以下代码使用我们的Rebex FTP for .NET。
using (Ftp client = new Ftp())
{
// connect and login to the FTP site
client.Connect("mirror.aarnet.edu.au");
client.Login("anonymous", "my@password");
// download all files
client.GetFiles(
"/pub/fedora/linux/development/i386/os/EFI/*",
"c:\\temp\\download",
FtpBatchTransferOptions.Recursive,
FtpActionOnExistingFiles.OverwriteAll
);
client.Disconnect();
}
The code is taken from my blogpost available at blog.rebex.net. The blogpost also references a sample which shows how ask the user how to handle each problem (e.g. Overwrite/Overwrite older/Skip/Skip all).
该代码取自我在blog.rebex.net上提供的博文。该博客文章还引用了一个示例,该示例显示了如何询问用户如何处理每个问题(例如,覆盖/覆盖旧/跳过/全部跳过)。
#2
Using C# FtpWebRequest and FtpWebReponse, you can use the following recursion (make sure the folder strings terminate in '\'):
使用C#FtpWebRequest和FtpWebReponse,您可以使用以下递归(确保文件夹字符串以'\'结尾):
public void GetAllDirectoriesAndFiles(string getFolder, string putFolder)
{
List<string> dirIitems = DirectoryListing(getFolder);
foreach (var item in dirIitems)
{
if ( item.Contains('.') )
{
GetFile(getFolder + item, putFolder + item);
}
else
{
var subDirPut = new DirectoryInfo(putFolder + "\\" + item);
subDirPut.Create();
GetAllDirectoriesAndFiles(getFolder + item + "\\", subDirPut.FullName + "\\");
}
}
}
The "item.Contains('.')" is a bit primitive, but has worked for my purposes. Post a comment if you need an example of the methods:
“item.Contains('。')”有点原始,但已经为我的目的而工作。如果您需要方法示例,请发表评论:
GetFile(string getFileAndPath, string putFileAndPath)
or
DirectoryListing(getFolder)
#3
For FTP protocol you can use FtpWebRequest
class from .NET framework. Though it does not have any explicit support for recursive file operations (including downloads). You have to implement the recursion yourself:
对于FTP协议,您可以使用.NET框架中的FtpWebRequest类。虽然它没有任何明确支持递归文件操作(包括下载)。你必须自己实现递归:
- List the remote directory
- Iterate the entries, downloading files and recursing into subdirectories (listing them again, etc.)
列出远程目录
迭代条目,下载文件并递归到子目录(再次列出它们等)
Tricky part is to identify files from subdirectories. There's no way to do that in a portable way with the FtpWebRequest
. The FtpWebRequest
unfortunately does not support the MLSD
command, which is the only portable way to retrieve directory listing with file attributes in FTP protocol. See also Checking if object on FTP server is file or directory.
棘手的部分是识别子目录中的文件。使用FtpWebRequest以便携方式无法做到这一点。遗憾的是,FtpWebRequest不支持MLSD命令,这是在FTP协议中检索具有文件属性的目录列表的唯一可移植方式。另请参阅检查FTP服务器上的对象是文件还是目录。
Your options are:
你的选择是:
- Do an operation on a file name that is certain to fail for file and succeeds for directories (or vice versa). I.e. you can try to download the "name". If that succeeds, it's a file, if that fails, it's a directory. But that can become a performance problem, when you have a large number of entries.
- You may be lucky and in your specific case, you can tell a file from a directory by a file name (i.e. all your files have an extension, while subdirectories do not)
- You use a long directory listing (
LIST
command =ListDirectoryDetails
method) and try to parse a server-specific listing. Many FTP servers use *nix-style listing, where you identify a directory by thed
at the very beginning of the entry. But many servers use a different format. The following example uses this approach (assuming the *nix format)
对文件名执行操作,该文件名肯定会对文件失败并对目录成功(反之亦然)。即你可以尝试下载“名称”。如果成功,它是一个文件,如果失败,它就是一个目录。但是当您有大量条目时,这可能会成为性能问题。
您可能很幸运,在您的特定情况下,您可以通过文件名告诉目录中的文件(即所有文件都有扩展名,而子目录没有)
您使用长目录列表(LIST命令= ListDirectoryDetails方法)并尝试解析特定于服务器的列表。许多FTP服务器使用* nix样式列表,您可以在条目的最开头通过d标识目录。但是许多服务器使用不同的格式。以下示例使用此方法(假设为* nix格式)
void DownloadFtpDirectory(string url, NetworkCredential credentials, string localPath)
{
FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url);
listRequest.UsePassive = true;
listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
listRequest.Credentials = credentials;
List<string> lines = new List<string>();
using (WebResponse listResponse = listRequest.GetResponse())
using (Stream listStream = listResponse.GetResponseStream())
using (StreamReader listReader = new StreamReader(listStream))
{
while (!listReader.EndOfStream)
{
lines.Add(listReader.ReadLine());
}
}
foreach (string line in lines)
{
string[] tokens =
line.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
string name = tokens[8];
string permissions = tokens[0];
string localFilePath = Path.Combine(localPath, name);
string fileUrl = url + name;
if (permissions[0] == 'd')
{
Directory.CreateDirectory(localFilePath);
DownloadFtpDirectory(fileUrl + "/", credentials, localFilePath);
}
else
{
FtpWebRequest downloadRequest = (FtpWebRequest)WebRequest.Create(fileUrl);
downloadRequest.UsePassive = true;
downloadRequest.UseBinary = true;
downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile;
downloadRequest.Credentials = credentials;
using (Stream ftpStream = downloadRequest.GetResponse().GetResponseStream())
using (Stream fileStream = File.Create(localFilePath))
{
ftpStream.CopyTo(fileStream);
}
}
}
}
The url
must be like:
网址必须如下:
-
ftp://example.com/
or -
ftp://example.com/path/
Or use 3rd party library that supports recursive downloads.
或者使用支持递归下载的第三方库。
For example with WinSCP .NET assembly you can download whole directory with a single call to Session.GetFiles
:
例如,使用WinSCP .NET程序集,只需调用Session.GetFiles即可下载整个目录:
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "example.com",
UserName = "user",
Password = "mypassword",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Download files
session.GetFiles("/home/user/*", @"d:\download\").Check();
}
Internally, WinSCP uses the MLSD
command, if supported by the server. If not, it uses the LIST
command and supports dozens of different listing formats.
在内部,如果服务器支持,WinSCP使用MLSD命令。如果没有,它使用LIST命令并支持许多不同的列表格式。
(I'm the author of WinSCP)
(我是WinSCP的作者)
#4
You could use System.Net.WebClient.DownloadFile()
, which supports FTP. MSDN Details here
您可以使用支持FTP的System.Net.WebClient.DownloadFile()。 MSDN详细信息在这里
#5
You can use FTPClient from laedit.net. It's under Apache license and easy to use.
您可以使用laedit.net中的FTPClient。它在Apache许可下并且易于使用。
It use FtpWebRequest :
它使用FtpWebRequest:
- first you need to use
WebRequestMethods.Ftp.ListDirectoryDetails
to get the detail of all the list of the folder - for each files you need to use
WebRequestMethods.Ftp.DownloadFile
to download it to a local folder
首先,您需要使用WebRequestMethods.Ftp.ListDirectoryDetails来获取文件夹的所有列表的详细信息
对于每个文件,您需要使用WebRequestMethods.Ftp.DownloadFile将其下载到本地文件夹
#1
downloading all files in a specific folder seems to be an easy task. However, there are some issues which has to be solved. To name a few:
下载特定文件夹中的所有文件似乎是一项简单的任务。但是,有一些问题需要解决。仅举几例:
- How to get list of files (System.Net.FtpWebRequest gives you unparsed list and directory list format is not standardized in any RFC)
- What if remote directory has both files and subdirectories. Do we have to dive into the subdirs and download it's content?
- What if some of the remote files already exist on the local computer? Should they be overwritten? Skipped? Should we overwrite older files only?
- What if the local file is not writable? Should the whole transfer fail? Should we skip the file and continue to the next?
- How to handle files on a remote disk which are unreadable because we don’t have sufficient access rights?
- How are the symlinks, hard links and junction points handled? Links can easily be used to create an infinite recursive directory tree structure. Consider folder A with subfolder B which in fact is not the real folder but the *nix hard link pointing back to folder A. The naive approach will end in an application which never ends (at least if nobody manage to pull the plug).
如何获取文件列表(System.Net.FtpWebRequest为您提供未解析的列表和目录列表格式在任何RFC中都未标准化)
如果远程目录同时包含文件和子目录,该怎么办?我们是否必须深入了解子目录并下载它的内容?
如果本地计算机上已存在某些远程文件,该怎么办?他们应该被覆盖吗?跳过?我们应该只覆盖旧文件吗?
如果本地文件不可写怎么办?整个转移是否会失败?我们应该跳过文件继续下一个吗?
如何处理远程磁盘上由于我们没有足够的访问权限而无法读取的文件?
如何处理符号链接,硬链接和连接点?链接可以很容易地用于创建无限递归目录树结构。考虑带有子文件夹B的文件夹A,实际上它不是真正的文件夹,而是指向文件夹A的* nix硬链接。天真的方法将在一个永不停止的应用程序中结束(至少如果没有人设法拔掉插件)。
Decent third party FTP component should have a method for handling those issues. Following code uses our Rebex FTP for .NET.
体面的第三方FTP组件应该有一个处理这些问题的方法。以下代码使用我们的Rebex FTP for .NET。
using (Ftp client = new Ftp())
{
// connect and login to the FTP site
client.Connect("mirror.aarnet.edu.au");
client.Login("anonymous", "my@password");
// download all files
client.GetFiles(
"/pub/fedora/linux/development/i386/os/EFI/*",
"c:\\temp\\download",
FtpBatchTransferOptions.Recursive,
FtpActionOnExistingFiles.OverwriteAll
);
client.Disconnect();
}
The code is taken from my blogpost available at blog.rebex.net. The blogpost also references a sample which shows how ask the user how to handle each problem (e.g. Overwrite/Overwrite older/Skip/Skip all).
该代码取自我在blog.rebex.net上提供的博文。该博客文章还引用了一个示例,该示例显示了如何询问用户如何处理每个问题(例如,覆盖/覆盖旧/跳过/全部跳过)。
#2
Using C# FtpWebRequest and FtpWebReponse, you can use the following recursion (make sure the folder strings terminate in '\'):
使用C#FtpWebRequest和FtpWebReponse,您可以使用以下递归(确保文件夹字符串以'\'结尾):
public void GetAllDirectoriesAndFiles(string getFolder, string putFolder)
{
List<string> dirIitems = DirectoryListing(getFolder);
foreach (var item in dirIitems)
{
if ( item.Contains('.') )
{
GetFile(getFolder + item, putFolder + item);
}
else
{
var subDirPut = new DirectoryInfo(putFolder + "\\" + item);
subDirPut.Create();
GetAllDirectoriesAndFiles(getFolder + item + "\\", subDirPut.FullName + "\\");
}
}
}
The "item.Contains('.')" is a bit primitive, but has worked for my purposes. Post a comment if you need an example of the methods:
“item.Contains('。')”有点原始,但已经为我的目的而工作。如果您需要方法示例,请发表评论:
GetFile(string getFileAndPath, string putFileAndPath)
or
DirectoryListing(getFolder)
#3
For FTP protocol you can use FtpWebRequest
class from .NET framework. Though it does not have any explicit support for recursive file operations (including downloads). You have to implement the recursion yourself:
对于FTP协议,您可以使用.NET框架中的FtpWebRequest类。虽然它没有任何明确支持递归文件操作(包括下载)。你必须自己实现递归:
- List the remote directory
- Iterate the entries, downloading files and recursing into subdirectories (listing them again, etc.)
列出远程目录
迭代条目,下载文件并递归到子目录(再次列出它们等)
Tricky part is to identify files from subdirectories. There's no way to do that in a portable way with the FtpWebRequest
. The FtpWebRequest
unfortunately does not support the MLSD
command, which is the only portable way to retrieve directory listing with file attributes in FTP protocol. See also Checking if object on FTP server is file or directory.
棘手的部分是识别子目录中的文件。使用FtpWebRequest以便携方式无法做到这一点。遗憾的是,FtpWebRequest不支持MLSD命令,这是在FTP协议中检索具有文件属性的目录列表的唯一可移植方式。另请参阅检查FTP服务器上的对象是文件还是目录。
Your options are:
你的选择是:
- Do an operation on a file name that is certain to fail for file and succeeds for directories (or vice versa). I.e. you can try to download the "name". If that succeeds, it's a file, if that fails, it's a directory. But that can become a performance problem, when you have a large number of entries.
- You may be lucky and in your specific case, you can tell a file from a directory by a file name (i.e. all your files have an extension, while subdirectories do not)
- You use a long directory listing (
LIST
command =ListDirectoryDetails
method) and try to parse a server-specific listing. Many FTP servers use *nix-style listing, where you identify a directory by thed
at the very beginning of the entry. But many servers use a different format. The following example uses this approach (assuming the *nix format)
对文件名执行操作,该文件名肯定会对文件失败并对目录成功(反之亦然)。即你可以尝试下载“名称”。如果成功,它是一个文件,如果失败,它就是一个目录。但是当您有大量条目时,这可能会成为性能问题。
您可能很幸运,在您的特定情况下,您可以通过文件名告诉目录中的文件(即所有文件都有扩展名,而子目录没有)
您使用长目录列表(LIST命令= ListDirectoryDetails方法)并尝试解析特定于服务器的列表。许多FTP服务器使用* nix样式列表,您可以在条目的最开头通过d标识目录。但是许多服务器使用不同的格式。以下示例使用此方法(假设为* nix格式)
void DownloadFtpDirectory(string url, NetworkCredential credentials, string localPath)
{
FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url);
listRequest.UsePassive = true;
listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
listRequest.Credentials = credentials;
List<string> lines = new List<string>();
using (WebResponse listResponse = listRequest.GetResponse())
using (Stream listStream = listResponse.GetResponseStream())
using (StreamReader listReader = new StreamReader(listStream))
{
while (!listReader.EndOfStream)
{
lines.Add(listReader.ReadLine());
}
}
foreach (string line in lines)
{
string[] tokens =
line.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
string name = tokens[8];
string permissions = tokens[0];
string localFilePath = Path.Combine(localPath, name);
string fileUrl = url + name;
if (permissions[0] == 'd')
{
Directory.CreateDirectory(localFilePath);
DownloadFtpDirectory(fileUrl + "/", credentials, localFilePath);
}
else
{
FtpWebRequest downloadRequest = (FtpWebRequest)WebRequest.Create(fileUrl);
downloadRequest.UsePassive = true;
downloadRequest.UseBinary = true;
downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile;
downloadRequest.Credentials = credentials;
using (Stream ftpStream = downloadRequest.GetResponse().GetResponseStream())
using (Stream fileStream = File.Create(localFilePath))
{
ftpStream.CopyTo(fileStream);
}
}
}
}
The url
must be like:
网址必须如下:
-
ftp://example.com/
or -
ftp://example.com/path/
Or use 3rd party library that supports recursive downloads.
或者使用支持递归下载的第三方库。
For example with WinSCP .NET assembly you can download whole directory with a single call to Session.GetFiles
:
例如,使用WinSCP .NET程序集,只需调用Session.GetFiles即可下载整个目录:
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "example.com",
UserName = "user",
Password = "mypassword",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Download files
session.GetFiles("/home/user/*", @"d:\download\").Check();
}
Internally, WinSCP uses the MLSD
command, if supported by the server. If not, it uses the LIST
command and supports dozens of different listing formats.
在内部,如果服务器支持,WinSCP使用MLSD命令。如果没有,它使用LIST命令并支持许多不同的列表格式。
(I'm the author of WinSCP)
(我是WinSCP的作者)
#4
You could use System.Net.WebClient.DownloadFile()
, which supports FTP. MSDN Details here
您可以使用支持FTP的System.Net.WebClient.DownloadFile()。 MSDN详细信息在这里
#5
You can use FTPClient from laedit.net. It's under Apache license and easy to use.
您可以使用laedit.net中的FTPClient。它在Apache许可下并且易于使用。
It use FtpWebRequest :
它使用FtpWebRequest:
- first you need to use
WebRequestMethods.Ftp.ListDirectoryDetails
to get the detail of all the list of the folder - for each files you need to use
WebRequestMethods.Ftp.DownloadFile
to download it to a local folder
首先,您需要使用WebRequestMethods.Ftp.ListDirectoryDetails来获取文件夹的所有列表的详细信息
对于每个文件,您需要使用WebRequestMethods.Ftp.DownloadFile将其下载到本地文件夹