I am trying to get full list of files, directories and sub-directories on tree view using StreamReader. The problem is it took too long and throws *"Operation time out exception" and shows only one level.
Here is my code
public void getServerSubfolder(TreeNode tv, string parentNode) {
string ptNode;
List<string> files = new List<string> ();
try {
FtpWebRequest request = (FtpWebRequest) WebRequest.
Create(parentNode);
request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
request.Credentials = new NetworkCredential(this.userName, this.Password);
request.UseBinary = true;
request.UsePassive = true;
request.Timeout = 10000;
request.ReadWriteTimeout = 10000;
request.KeepAlive = false;
FtpWebResponse response = (FtpWebResponse) request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
string fileList;
string[] fileName;
//MessageBox.Show(reader.ReadToEnd().ToString());
while (!reader.EndOfStream) {
fileList = reader.ReadLine();
fileName = fileList.Split(' ');
if (fileName[0] == "drwxr-xr-x") {
// if it is directory
TreeNode tnS = new TreeNode(fileName[fileName.Length - 1]);
tv.Nodes.Add(tnS);
ptNode = parentNode + "/" + fileName[fileName.Length - 1] + "/";
getServerSubfolder(tnS, ptNode);
} else files.Add(fileName[fileName.Length - 1]);
}
reader.Close();
response.Close();
} catch (Exception ex) {
MessageBox.Show("Sub--here " + ex.Message + "----" + ex.StackTrace);
}
}
You have to read (and cache) whole listing before recursing into subdirectories, otherwise the top-level request will timeout before you complete subdirectories listing.
You can keep using ReadLine, no need to use ReadToEnd and separate the lines yourself.
void ListFtpDirectory(
string url, string rootPath, NetworkCredential credentials, List<string> list)
{
FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url + rootPath);
listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
listRequest.Credentials = credentials;
List<string> lines = new List<string>();
using (FtpWebResponse listResponse = (FtpWebResponse)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 filePath = rootPath + name;
if (permissions[0] == 'd')
{
ListFtpDirectory(url, filePath + "/", credentials, list);
}
else
{
list.Add(filePath);
}
}
}
Use the function like:
List<string> list = new List<string>();
NetworkCredential credentials = new NetworkCredential("user", "mypassword");
string url = "ftp://ftp.example.com/";
ListFtpDirectory(url, "", credentials, list);
A disadvantage of the above approach is that it has to parse server-specific listing to retrieve information about files and folders. The code above expects a common *nix-style listing. But many servers use a different format.
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.
If you want to avoid troubles with parsing the server-specific directory listing formats, use a 3rd party library that supports the MLSD command and/or parsing various LIST listing formats; and recursive downloads.
For example with WinSCP .NET assembly you can list whole directory with a single call to the Session.EnumerateRemoteFiles:
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "user",
Password = "mypassword",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// List files
IEnumerable<string> list =
session.EnumerateRemoteFiles("/", null, EnumerationOptions.AllDirectories).
Select(fileInfo => fileInfo.FullName);
}
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.
(I'm the author of WinSCP)
I'm doing similar things, but instead of using StreamReader.ReadLine() for each one, I get it all at once with StreamReader.ReadToEnd(). You don't need ReadLine() to just get a list of directories. Below is my entire code (Entire howto is explained in this tutorial):
FtpWebRequest request=(FtpWebRequest)FtpWebRequest.Create(path);
request.Method=WebRequestMethods.Ftp.ListDirectoryDetails;
List<ftpinfo> files=new List<ftpinfo>();
//request.Proxy = System.Net.WebProxy.GetDefaultProxy();
//request.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
request.Credentials = new NetworkCredential(_username, _password);
Stream rs=(Stream)request.GetResponse().GetResponseStream();
OnStatusChange("CONNECTED: " + path, 0, 0);
StreamReader sr = new StreamReader(rs);
string strList = sr.ReadToEnd();
string[] lines=null;
if (strList.Contains("\r\n"))
{
lines=strList.Split(new string[] {"\r\n"},StringSplitOptions.None);
}
else if (strList.Contains("\n"))
{
lines=strList.Split(new string[] {"\n"},StringSplitOptions.None);
}
//now decode this string array
if (lines==null || lines.Length == 0)
return null;
foreach(string line in lines)
{
if (line.Length==0)
continue;
//parse line
Match m= GetMatchingRegex(line);
if (m==null) {
//failed
throw new ApplicationException("Unable to parse line: " + line);
}
ftpinfo item=new ftpinfo();
item.filename = m.Groups["name"].Value.Trim('\r');
item.path = path;
item.size = Convert.ToInt64(m.Groups["size"].Value);
item.permission = m.Groups["permission"].Value;
string _dir = m.Groups["dir"].Value;
if(_dir.Length>0 && _dir != "-")
{
item.fileType = directoryEntryTypes.directory;
}
else
{
item.fileType = directoryEntryTypes.file;
}
try
{
item.fileDateTime = DateTime.Parse(m.Groups["timestamp"].Value);
}
catch
{
item.fileDateTime = DateTime.MinValue; //null;
}
files.Add(item);
}
return files;
Related
My current code downloads a SharePoint file from an absolute URL of the file and writes to local.
I want to change it to use the folder URL instead and downloads file in the folder base on some filter.
Can it be done?
Below is my current code snippet:
string fullFilePath = DownloadSPFile("http://MySharePointSite.com/sites/Collection1/Folder1/File1.docx/");
public static string DownloadSPFile(string urlPath)
{
string serverTempdocPath = "";
try
{
var request = (HttpWebRequest)WebRequest.Create(urlPath);
var credentials = new NetworkCredential("username", "password", "domain");
request.Credentials = credentials;
request.Timeout = 20000;
request.AllowWriteStreamBuffering = false;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
serverTempdocPath = Path.Combine(AppConfig.EmailSaveFilePath + "_DOWNLOADED.eml");
using (FileStream fs = new FileStream(serverTempdocPath, FileMode.Create))
{
byte[] read = new byte[256];
int count = stream.Read(read, 0, read.Length);
while (count > 0)
{
fs.Write(read, 0, count);
count = stream.Read(read, 0, read.Length);
}
}
}
}
}
catch (Exception ex)
{
AppLogger.LogError(ex, "");
throw ex;
}
return serverTempDocPath;
}
If your sharepoint site enables sharepoint rest api, you can get the details very easy.
Get list of files
url: http://site url/_api/web/GetFolderByServerRelativeUrl('/Folder Name')/Files
method: GET
headers:
Authorization: "Bearer " + accessToken
accept: "application/json;odata=verbose" or "application/atom+xml"
and pass query for that
"?filter=$top=1&$orderby=Created desc"
More information
Has you try to format your urlPath ?
like:
string urlPath = "path/to/folder/";
string fileFilter = "Cat.png";
string finalPath= String.format("{0}{1}", urlPath, fileFilter);
(Or anything like this)
(Hope I understand your question and I was able to help you ^^)
One day, FileZilla decided not to let me download. (Right click, "Download" is gray), so I downloaded SmartFTP, but I didn't really like that.
So I asked myself, why not make one? So, I already have it half-working (can connect to ftp-servers, download, upload, moving in and out folders, and create directories), however, it can't open folders with accented and/or special characters in them (á, é, ö, #, etc.), and they also appear in the listbox like this: "Adatb?ziskezel?s", while the inbuilt stuff in windows shows it like this: "Adatbáziskezelés".
What could I do in order to make it work?
public string[] OpenFolder(string foldername)
{
byte[] bytes = Encoding.Default.GetBytes(foldername);
foldername = Encoding.UTF8.GetString(bytes);
string[] downloadFiles;
StringBuilder result = new StringBuilder();
FtpWebRequest reqFTP;
try
{
reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(
"ftp://" + IP + "/"+foldername+"/"));
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential(username,
password);
reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
WebResponse response = reqFTP.GetResponse();
StreamReader reader = new StreamReader(response
.GetResponseStream());
string line = reader.ReadLine();
while (line != null)
{
result.Append(line);
result.Append("\n");
line = reader.ReadLine();
}
result.Remove(result.ToString().LastIndexOf('\n'), 1);
reader.Close();
response.Close();
return result.ToString().Split('\n');
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
downloadFiles = null;
return downloadFiles;
}
}
if (line.Contains("#"))
{
line = line.Replace("#", Uri.HexEscape('#'));
}
and so forth...
I want to get the list of files from FTP server with specific search pattern ( Ex: get all the files having pattern as "*.txt") and download these files only using C#.net.
Below is the code returning list of files from FTP server, please suggest the addition code required to achieve the required task.
FtpWebRequest reqFTP;
reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + coldata.Host + "/"));
//("ftp://" + coldata.host + "/"));
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential(coldata.Uid, coldata.Pwd);
reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
reqFTP.Timeout = System.Threading.Timeout.Infinite;
reqFTP.Proxy = null;
reqFTP.KeepAlive = true;
reqFTP.UsePassive = true;
FtpWebResponse res = (FtpWebResponse)reqFTP.GetResponse();
response = reqFTP.GetResponse();
reader = new StreamReader(response.GetResponseStream());
string line = reader.ReadLine();
while (line != null)
{
result.Append(line);
result.Append("\n");
line = reader.ReadLine();
}
// to remove the trailing '\n'
result.Remove(result.ToString().LastIndexOf('\n'), 1);
downloadRes = true;
return result.ToString().Split('\n');
Thanks.
You could use System.IO.Path.GetExtension, something like this maybe:
while (line != null)
{
if (System.IO.Path.GetExtension(line) == "txt")
{
result.Append(line);
result.Append("\n");
line = reader.ReadLine();
}
}
Not exactly what you asked for, but you cannot specify search patterns for FTP, see here:
how to fetch a range of files from an FTP server using C#
I am getting files from ftp folder like this.
request = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://ipaddress/foldername"));
request.UseBinary = true;
request.Credentials = new NetworkCredential("username", "password");
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.UseBinary = true;
WebResponse response = request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
string line = reader.ReadLine();
while (line != null)
{
result.Append(line);
result.Append("\n");
line = reader.ReadLine();
}
// to remove the trailing '\n'
result.Remove(result.ToString().LastIndexOf('\n'), 1);
reader.Close();
response.Close();
string[] results = result.ToString().Split('\n');
foreach (string filename in results)
{
connetionString = "Data Source=testservername;Initial Catalog=testdb;User ID=sa;Password=sa";
connection = new SqlConnection(connetionString);
FtpWebRequest request1 = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://servername/" + filename));
request1.Method = WebRequestMethods.Ftp.DownloadFile;
request1.UseBinary = true;
request1.Credentials = new NetworkCredential("username", "password");
responses = (FtpWebResponse)request1.GetResponse();
StreamReader reader1 = new StreamReader(responses.GetResponseStream());
string xml = reader1.ReadToEnd();
ds.ReadXml(new XmlTextReader(new StringReader(xml)));
int i = 0;
connection.Open();
for (i = 0; i <= ds.Tables[1].Rows.Count - 1 ; i++)
{
sql = "insert into Details values('" + ds.Tables[1].Rows[i].ItemArray[0]
+ "')";
command = new SqlCommand(sql, connection);
adpter.InsertCommand = command;
adpter.InsertCommand.ExecuteNonQuery();
}
FtpWebRequest requestDelete = (FtpWebRequest)WebRequest.Create("ftp://sername/" + filename);
requestDelete.Method = WebRequestMethods.Ftp.DeleteFile;
requestDelete.UseBinary = true;
requestDelete.Credentials = new NetworkCredential("username", "password");
responseDelete = (FtpWebResponse)requestDelete.GetResponse();
try
{
string res = responseDelete.StatusDescription;
if (res == "250 Delete operation successful.")
{
MessageBox.Show("Done......");
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
responseDelete.Close();
connection.Close();
responses.Close();
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
Hi need to get files from ftp folder and read that xml files one by one and save it as records in my Databse after that need to delete that file ftp folder.
if for testing i placed two files in ftp folder first files reading successfully .but getting error for reading second file like "Object Reference is not set instance of object "in the below line.
ds.ReadXml(new XmlTextReader(new StringReader(xml)));
please tell me any thing wrong in my code.i am sending ftp request multiple times it is good or suggest me by seeing above lines of code how to do that?
I want to delete a folder in FTP and it's files recursively.
Any example code do I can implement?
First you have to list all your files in your directory :
public static List<string> DirectoryListing(string Path, string ServerAdress, string Login, string Password)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://" + ServerAdress + Path);
request.Credentials = new NetworkCredential(Login, Password);
request.Method = WebRequestMethods.Ftp.ListDirectory;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
List<string> result = new List<string>();
while (!reader.EndOfStream)
{
result.Add(reader.ReadLine());
}
reader.Close();
response.Close();
return result;
}
Then you need a method to delete a single file (because you can delete a folder only if it's empty) :
public static void DeleteFTPFile(string Path, string ServerAdress, string Login, string Password)
{
FtpWebRequest clsRequest = (System.Net.FtpWebRequest)WebRequest.Create("ftp://" + ServerAdress + Path);
clsRequest.Credentials = new System.Net.NetworkCredential(Login, Password);
clsRequest.Method = WebRequestMethods.Ftp.DeleteFile;
string result = string.Empty;
FtpWebResponse response = (FtpWebResponse)clsRequest.GetResponse();
long size = response.ContentLength;
Stream datastream = response.GetResponseStream();
StreamReader sr = new StreamReader(datastream);
result = sr.ReadToEnd();
sr.Close();
datastream.Close();
response.Close();
}
And finally :
public static void DeleteFTPDirectory(string Path, string ServerAdress, string Login, string Password)
{
FtpWebRequest clsRequest = (System.Net.FtpWebRequest)WebRequest.Create("ftp://" + ServerAdress + Path);
clsRequest.Credentials = new System.Net.NetworkCredential(Login, Password);
List<string> filesList = DirectoryListing(Path, ServerAdress, Login, Password);
foreach (string file in filesList)
{
DeleteFTPFile(Path + file, ServerAdress, Login, Password);
}
clsRequest.Method = WebRequestMethods.Ftp.RemoveDirectory;
string result = string.Empty;
FtpWebResponse response = (FtpWebResponse)clsRequest.GetResponse();
long size = response.ContentLength;
Stream datastream = response.GetResponseStream();
StreamReader sr = new StreamReader(datastream);
result = sr.ReadToEnd();
sr.Close();
datastream.Close();
response.Close();
}
You can easily call this like that (for me those methods are in a class called "Ftp") :
Ftp.DeleteFTPDirectory(the_path_of_your_folder_in_ftp,your_server_address,your_ftp_login,your_ftp_password);
Of course, you'll need to customize those lines, but it worked perfectly for me :)
There's no support for recursive operations in the FtpWebRequest class (or any other FTP implementation in the .NET framework). You have to implement the recursion yourself:
List the remote directory
Iterate the entries, deleting 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.
Your options are:
Do an operation on a file name that is certain to fail for a file and to succeed for directory (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 the d at the very beginning of the entry. But many servers use a different format. The following example uses this approach (assuming the *nix format).
In this specific case, you can just try to delete the entry as a file. If deleting fails, try to list the entry as a directory. If the listing succeeds, you assume it's a folder and proceed accordingly. Unfortunately some servers do not error, when you try to list a file. They will just return a listing with a single entry for the file.
static void DeleteFtpDirectory(string url, NetworkCredential credentials)
{
FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url);
listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
listRequest.Credentials = credentials;
List<string> lines = new List<string>();
using (FtpWebResponse listResponse = (FtpWebResponse)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 fileUrl = url + name;
if (permissions[0] == 'd')
{
DeleteFtpDirectory(fileUrl + "/", credentials);
}
else
{
FtpWebRequest deleteRequest = (FtpWebRequest)WebRequest.Create(fileUrl);
deleteRequest.Method = WebRequestMethods.Ftp.DeleteFile;
deleteRequest.Credentials = credentials;
deleteRequest.GetResponse();
}
}
FtpWebRequest removeRequest = (FtpWebRequest)WebRequest.Create(url);
removeRequest.Method = WebRequestMethods.Ftp.RemoveDirectory;
removeRequest.Credentials = credentials;
removeRequest.GetResponse();
}
The url should be like ftp://example.com/directory/to/delete/
Or use a 3rd party library that supports recursive operations.
For example with WinSCP .NET assembly you can delete whole directory with a single call to Session.RemoveFiles:
// 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);
// Delete folder
session.RemoveFiles("/home/user/foldertoremove").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.
(I'm the author of WinSCP)
Nice Little example you can find here:
http://msdn.microsoft.com/en-us/library/system.net.ftpwebrequest.aspx
In the example they used WebRequestMethods.Ftp.UploadFile class to direct what kind of operation they want to do.
Use the WebRequestMethods.Ftp.RemoveDirectory method once you have a handle on the parent directory you want to delete:
http://msdn.microsoft.com/en-us/library/system.net.webrequestmethods.ftp.aspx
None of the solutions really worked on different types of servers except using
System.Net.FtpClient
using System.Net.FtpClient;
static void DeleteFtpDirectoryAndContent(string host, string path, NetworkCredential credentials, string dontDeleteFileUrl)
{
using (FtpClient conn = new FtpClient())
{
conn.Host = host;
conn.Credentials = credentials;
foreach (FtpListItem item in conn.GetListing(path, FtpListOption.AllFiles | FtpListOption.ForceList))
{
switch (item.Type)
{
case FtpFileSystemObjectType.Directory:
conn.DeleteDirectory(item.FullName, true, FtpListOption.AllFiles | FtpListOption.ForceList);
break;
case FtpFileSystemObjectType.File:
if (!dontDeleteFileUrl.EndsWith(item.FullName, StringComparison.InvariantCultureIgnoreCase))
conn.DeleteFile(item.FullName);
break;
}
}
}
}