create a directory with subdirectories in a single ftp request? - c#

I got a small problem on how to create a directory with subdirectories in a single ftp request
I have a string s
string s = "a/b/c/d"
NOTE : The words between slashes are random and the number of items is unknown.
How to create in the FTP server the directory a/b/c/d ????
The way i'm using to achieve this is to split the string and create a folder for each part using the code below :
var ftpWebRequest = (FtpWebRequest)WebRequest.Create("ftp://domain.com/public_html/a");
ftpWebRequest.Credentials = new NetworkCredential(ftpUsername, ftpPassword);
ftpWebRequest.Method = WebRequestMethods.Ftp.MakeDirectory;
ftpWebRequest.GetResponse();
Then i create the b directory inside a, then c inside b, then d inside c by repeating some code, each time
I tried to type the url directly. like :
var ftpWebRequest = (FtpWebRequest)WebRequest.Create("ftp://doemin.com/public_html/a/b/c/d);
but it doesn't work.
Is there a short way on how can i create a folder with other subdirectories in a one single request ?

If you are willing to use a more friendly library (free and open source) like this one:
System.Net.FtpClient.dll
then you could write code like this (adapted from their example)
static ManualResetEvent m_reset = new ManualResetEvent(false);
void Main()
{
m_reset.Reset();
using (FtpClient ftp = new FtpClient())
{
ftp.Host = "yourFTPHost.com";
ftp.Credentials = new NetworkCredential("yourUserName", "yourPassword");
ftp.SetWorkingDirectory("/rootForTest");
if(ftp.DirectoryExists("test"))
ftp.DeleteDirectory("test", true);
ftp.BeginCreateDirectory("test/path/that/should/be/created", true,
new AsyncCallback(CreateDirectoryCallback), ftp);
m_reset.WaitOne();
ftp.Disconnect();
}
}
static void CreateDirectoryCallback(IAsyncResult ar)
{
FtpClient ftp = ar.AsyncState as FtpClient;
try
{
if (ftp == null)
throw new InvalidOperationException("The FtpControlConnection object is null!");
ftp.EndCreateDirectory(ar);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
m_reset.Set();
}
}
A side note: System.Net.FtpClient requires the full NET 4.0 Framework. (Cliente Profile is not enough)

Some servers do support FTP commands like MKD a/b/c/d . If your server doesn't, but supports execution of shell commands via SITE command, you can try to call "SITE md a/b/c/d" (but this is machine-specific). If none of the above works, then you have to create folders in a loop like you do or use some library which hides this loop in a one method.

Related

Dropbox.Api failing to upload large files

I am uploading files to dropbox using the following code.
I am using the nuget package Dropbox.Api and getting the exception System.Threading.Tasks.TaskCanceledException("A task was canceled.")
From this SO Question it appears to be a timeout issue.
So how do I modify the following code to set the timeout.
public async Task<FileMetadata> UploadFileToDropBox(string fileToUpload, string folder)
{
DropboxClient client = new DropboxClient(GetAccessToken());
using (var mem = new MemoryStream(File.ReadAllBytes(fileToUpload)))
{
string filename = Path.GetFileName(fileToUpload);
try
{
string megapath = GetFullFolderPath(folder);
string megapathWithFile = Path.Combine(megapath, Path.GetFileName(Path.GetFileName(filename))).Replace("\\", "/");
var updated = client.Files.UploadAsync(megapathWithFile, WriteMode.Overwrite.Instance, body: mem);
await updated;
return updated.Result;
}
catch (Exception ex)
{
return null;
}
}
}
Try creating and initializing the client like this:
var config = new DropboxClientConfig();
config.HttpClient.Timeout = new TimeSpan(hr, min, sec); // choose values
var client = DropboxClient(GetAccessToken(), config);
Reference:
http://dropbox.github.io/dropbox-sdk-dotnet/html/M_Dropbox_Api_DropboxClient__ctor_1.htm
One more thing to keep in mind is UploadAsync will not work for files larger than 150MB as per documentation. One will have to use UploadSessionStartAsync based implementation for it. I was making the mistake without realizing it and it took ages for me to fish the problem out.

.Net (C#) SSRS (rdl) file not getting imported from C:\ to ReportServer

The code that you see was taken from MSDN. Upon testing it says invalid path for SOURCE. I agree. They want the source to be a URL in another Reporting Server. However what I need is to be able to copy a RDL file from my C:\ to the Reporting Server. How can I ?
static void move_report(string currentPath, string targetPath )
{
ReportingService2010 service = new ReportingService2010();
ReportingService2010 rs = new ReportingService2010();
rs.Url = "http://MHPDW2/ReportServer/" + "ReportService2010.asmx";
rs.Credentials = System.Net.CredentialCache.DefaultCredentials;
try
{
rs.MoveItem(currentPath, targetPath);
}
catch (SoapException e)
{
Console.WriteLine(e.Detail.OuterXml);
}
}
static void Main(string[] args)
{
string currentPath = "H:\\ITTFS2012\\SSRS\\DW15Reports\\Claims\\6.1 universe.rdl";
string targetPath = "http://MHPDW2/ReportServer/MidwestHealthPlan/Claims/HPMS/MCR Plan Code/H5685 2014 HPMS/";
move_report(currentPath,targetPath);
What does "H:\" drive resolve to and do you have access to the source location?
Team:
I found the answer. Everything is here ( including code )
I had to use the rs.CreateCatalogItem method.
https://msdn.microsoft.com/en-us/library/reportservice2010.reportingservice2010.createcatalogitem.aspx

Code to Determine if FTP Directory Exists Suddenly Stopped Working

I wrote the following code a long time ago to determine if an FTP directory exists:
public bool DirectoryExists(string directory)
{
try
{
FtpWebRequest request = GetRequest(directory);
request.Method = WebRequestMethods.Ftp.ListDirectory;
using (FtpWebResponse response = request.GetResponse() as FtpWebResponse)
{
StreamReader sr = new StreamReader(response.GetResponseStream(), System.Text.Encoding.ASCII);
sr.ReadToEnd();
sr.Close();
response.Close();
}
return true;
}
catch { }
return false;
}
protected FtpWebRequest GetRequest(string filename = "")
{
FtpWebRequest request = WebRequest.Create(_host.GetUrl(filename)) as FtpWebRequest;
request.Credentials = new NetworkCredential(Username, Password);
request.Proxy = null;
request.KeepAlive = false;
return request;
}
This code has worked for several years, but today it doesn't. When testing a directory that does not exist, the code in DirectoryExists() no longer throws an exception, and the method incorrectly returns true.
If I assign the results of sr.ReadToEnd() to a string, it is an empty string.
In this case, the code _host.GetUrl(filename) returned "ftp://www.mydomain.com/Articles/winforms/accessing-the-windows-registry". This is the expected value. And still my DirectoryExists() method does not throw an exception when this path does not exist on the server. I even passed this non-existing directory to a method that uses WebRequestMethods.Ftp.ListDirectoryDetails to build a directory listing. This method simply returns an empty listing and also throws no exception.
I believe I first encountered this issue when I moved my code to a new computer with Visual Studio 2013. I'm using .NET 4.5 and got the same behavior when using .NET 4.5.1.
Questions:
Why doesn't this code, which has worked for years and uses the same technique used on most of the online examples I found, work? And what could possibly cause this code to suddenly stop working?
Is there a way to detect for the presence of a directory that works? I suppose the other approach is to scan the parent directory, although the logic would need to be different when the routine is supposed to verify the root directory.
I managed to reproduce your error on another site I have access to. After some playing around, here's my conclusion:-
When you make a FtpWebRequest with a FTP URL that does NOT end with a /, such as:
ftp://ftp.someftp.com/somefolder/invalidfolder
AND you specified WebRequestMethods.Ftp.ListDirectory as the method, then what it does behind the scene is to run the following command:
NLST "somefolder/invalidfolder"
Normally, NLST will list the contents of the specified folder, and throws an exception if the folder does not exist. But because you did not specify a / at the end of invalidfolder, NLST will think that invalidfolder may actually be a file (or a filename pattern). If it manages to find a folder named invalidfolder, then and only then will it treat it as a folder. Otherwise it will try to search a file named invalidfolder underneath the parent folder somefolder. If the file does not exist, then one of the following will occur, depending on which FTP server software (and its configurations) is running:
It throws an ERROR 550: File or Folder not found. (Example: spftp v1.0)
It returns an empty result. (Example: vsFTPd v2.0.5)
In your case, the FTP server returns the latter response, and your code falls over.
The solution? Just add some validation code to make sure the ftp folder you are trying to access always has a / at the end. Something like the following:-
if (!directory.EndsWith('/'))
directory += '/';
In effect ftp class has strange beahvior however you should achieve your goal in this way using a simple console application project or simply in your original project too.
VBNET VERSION
Sub Main()
Dim ftp As New FtpSystem
If ftp.DirectoryExist("p") Then
Console.WriteLine("false")
Else
Console.WriteLine("true")
End If
Console.ReadLine()
End Sub
Private Class FtpSystem
Public Function DirectoryExist(ByVal dir As String)
Dim uri As New Uri("ftp://domain.com" & "/" & dir)
Dim netCred As New NetworkCredential("user", "password")
Dim ftprq As FtpWebRequest = FtpWebRequest.Create(uri)
With ftprq
.Credentials = netCred
.KeepAlive = True
.Method = WebRequestMethods.Ftp.ListDirectory
.UsePassive = True
.UseBinary = False
End With
Dim ftprs As FtpWebResponse = Nothing
Dim Sr As StreamReader = Nothing
'if ftp try to matching folder ad if it will succeed then return true if not it will thrown an exception
'and it will return false. Is not a good way because it should be implement a very granular exception matching but
'for test is best simple approach.
Try
ftprs = DirectCast(ftprq.GetResponse, FtpWebResponse)
Sr = New StreamReader(ftprs.GetResponseStream)
Dim T As String = Sr.ReadToEnd
Console.Write(T)
Return True
Catch ex As Exception
Return False
End Try
End Function
End Class
C# VERSION(TRANSLATE ON THE FLY WITH ONLINE TOOL PLEASE CHECK CODE)
public void Main()
{
FtpSystem ftp = new FtpSystem();
if (ftp.DirectoryExist("p")) {
Console.WriteLine("false");
} else {
Console.WriteLine("true");
}
Console.ReadLine();
}
private class FtpSystem
{
public object DirectoryExist(string dir)
{
Uri uri = new Uri("ftp://domain.com" + "/" + dir);
NetworkCredential netCred = new NetworkCredential("user", "password");
FtpWebRequest ftprq = FtpWebRequest.Create(uri);
var _with1 = ftprq;
_with1.Credentials = netCred;
_with1.KeepAlive = true;
_with1.Method = WebRequestMethods.Ftp.ListDirectory;
_with1.UsePassive = true;
_with1.UseBinary = false;
FtpWebResponse ftprs = null;
StreamReader Sr = null;
//if ftp try to matching folder ad if it will succeed then return true if not it will thrown an exception
//and it will return false. Is not a good way because it should be implement a very granular exception matching but
//for test is best simple approach.
try {
ftprs = (FtpWebResponse)ftprq.GetResponse;
Sr = new StreamReader(ftprs.GetResponseStream);
string T = Sr.ReadToEnd;
Console.Write(T);
return true;
} catch (Exception ex) {
return false;
}
}
}
I hope that this help you.Bye
As mentionned above, perhaps the FTP server configuration changed. Have you tried to explicitly set the value of the UsePassive or UseBinary properties either ways ?
You can try sending ftp commands to the ftp server:
ProcessStartInfo psi = new ProcessStartInfo("cmd.exe");
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardError = true;
Process proc = Process.Start(psi);
// Open the batch file for reading
// Attach the output for reading
StreamReader sOut = proc.StandardOutput;
StreamReader sErr = proc.StandardError;
// Attach the in for writing
StreamWriter sIn = proc.StandardInput;
// Write each line of the batch file to standard input
while (sr.Peek() != -1)
{
sIn.WriteLine("ftp");
sIn.WriteLine("open remote-server-name");
sIn.WriteLine("username");
sIn.WriteLine("password");
sIn.WriteLine("ls");
}
string outPutStuff= sOut.ReadToEnd();
proc.WaitForExit(); //this does not appear to be needed.
string outErrStuff = sErr.ReadToEnd();
Console.WriteLine("Start FileTransfer FTP Output");
Console.WriteLine(outPutStuff);
Console.WriteLine("Any errors follow this line---");
Console.WriteLine(outErrStuff);
Console.WriteLine(outPutStuff);
sOut.Close();
sIn.Close();
Please,check if there is a file named accessing-the-windows-registry (no file extension) in winforms folder.
_host.GetUrl(filename) should return a string ended with a "/". When there is file with the same name as the intended folder name, no exception will be thrown.
If you are not getting exceptions, as a possible work-around, you could try requesting a predictable attribute of the folder, like the date it was created.
Another suggestion is using WireShark to see what FTP requests are being made behind the scenes, which will let you see if it's .NET or the server returning unhelpful responses.

Top level domain ignorance at using RIPE

I'm developing a simple app in c#, that can check if a domain name is available to puchase for a specific tld.
The method: I downloaded a whois-server list, I send the domain name to its whois server with a TCP client on the protocol 43, and check the servers answer.
The problem: more countries has the same whois server: "whois.ripe.net" .
If I send the full domain name(with tld), the server's answer is always "No entries found in source RIPE.". If I send the domain name without tld, I dont get any tld specific data about the status of the domain name.
The method I use:
private string GetWhoisInformation(string whoisServer, string url)
{
try
{
StringBuilder stringBuilderResult = new StringBuilder();
TcpClient tcpClinetWhois = new TcpClient(whoisServer, 43);
NetworkStream networkStreamWhois = tcpClinetWhois.GetStream();
BufferedStream bufferedStreamWhois = new BufferedStream(networkStreamWhois);
StreamWriter streamWriter = new StreamWriter(bufferedStreamWhois);
streamWriter.WriteLine(url);
streamWriter.Flush();
StreamReader streamReaderReceive = new StreamReader(bufferedStreamWhois);
while (!streamReaderReceive.EndOfStream)
stringBuilderResult.AppendLine(streamReaderReceive.ReadLine());
return stringBuilderResult.ToString();
}
catch
{
return "lekérdezés sikertelen";
}
}
Example:
I do:
GetWhoisInformation("whois.ripe.net", "pokerstars.hu")
The server's answer:
%ERROR:101: no entries found
%
% No entries found in source RIPE.
for the next command:
GetWhoisInformation("whois.ripe.net", "pokerstars")
the result contains several blocks like this:
% Information related to '80.65.254.128 - 80.65.254.159'
inetnum: 80.65.254.128 - 80.65.254.159
netname: Pokerstars
descr: Hosting
country: GB
admin-c: DC77-RIPE
tech-c: JM2352-RIPE
status: assigned PA
mnt-by: manx-telecom-mnt
changed: bill.hogg#manx-telecom.com 20101123
source: RIPE
There's no information about the domain name "pokerstars.hu". Of course, I get exactly the same answers if I want to check pokerstars.va. Pokerstars.hu is a registred domain, pokerstars.va is not.
How can I find the correct status of a domain name?
RIPE does not serve as a ccTLD whois server for any domains; like ARIN, it contains only netblock information. Each ccTLD has its own root whois server (or, that is, some of them don't have a proper whois service -- for example, the Spanish .es registry requires that you use a web client, with an obnoxious CAPTCHA you have to fill in every time).
See also http://www.ripe.net/data-tools/db although it is not very explicit about what the database does not contain.
You can get the address of the authoritative whois server by requesting the ccTLD's information from whois.iana.org.
vnix$ whois -h whois.iana.org hu | fgrep whois:
whois: whois.nic.hu
See also http://www.iana.org/domains/root/db/
I tried your code against whois.melbourneit.net and it found one of my domains no trouble. I was able to reproduce your problem running against RIPE and so I tried the same query interactively on their website - and had the same result. There's nothing wrong with your code.
tripleee is right about whois.nic.hu, I successfully used it to resolve pokerstars.hu - which leaves me wondering what the blazes is purpose of the RIPE whois server.
Thanks to triplee for showing us how to obtain the whois server friendly-name for a ccTLD.
You may find this useful:
using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
namespace Whois
{
class Program
{
static void Main(string[] args)
{
string tldWhoisServer = "whois.iana.org";
string ccTldServer, query = null;
Console.Write("Query> ");
while ((query = Console.ReadLine()) != string.Empty)
{
string tld = query.Substring(query.LastIndexOf('.') + 1);
string foo = GetWhoisInformation(tldWhoisServer, tld);
foo = foo.Remove(0, foo.IndexOf("whois:") + 6).TrimStart();
ccTldServer = foo.Substring(0, foo.IndexOf('\r'));
Console.WriteLine(GetWhoisInformation(ccTldServer, query));
Console.Write("Query> ");
}
}
static string GetWhoisInformation(string whoisServer, string url)
{
try
{
StringBuilder stringBuilderResult = new StringBuilder();
TcpClient tcpClinetWhois = new TcpClient(whoisServer, 43);
NetworkStream networkStreamWhois = tcpClinetWhois.GetStream();
BufferedStream bufferedStreamWhois = new BufferedStream(networkStreamWhois);
StreamWriter streamWriter = new StreamWriter(bufferedStreamWhois);
streamWriter.WriteLine(url);
streamWriter.Flush();
StreamReader streamReaderReceive = new StreamReader(bufferedStreamWhois);
while (!streamReaderReceive.EndOfStream)
stringBuilderResult.AppendLine(streamReaderReceive.ReadLine());
return stringBuilderResult.ToString();
}
catch
{
return "Query failed";
}
}
}
}

How to send arbitrary FTP commands in C#

I have implemented the ability to upload, download, delete, etc. using the FtpWebRequest class in C#. That is fairly straight forward.
What I need to do now is support sending arbitrary FTP commands such as
quote SITE LRECL=132 RECFM=FB
or
quote SYST
Here's an example configuration straight from our app.config:
<!-- The following commands will be executed before any uploads occur -->
<extraCommands>
<command>quote SITE LRECL=132 RECFM=FB</command>
</extraCommands>
I'm still researching how to do this using FtpWebRequest. I'll probably try WebClient class next. Anyone can point me in the right direction quicker? Thanks!
UPDATE:
I've come to that same conclusion, as of .NET Framework 3.5 FtpWebRequest doesn't support anything except what's in WebRequestMethods.Ftp.*. I'll try a third party app recommended by some of the other posts. Thanks for the help!
I don't think it can be done with FtpWebRequest... The only way to specify a FTP command is through the Method property, and the documentation states :
Note that the strings defined in the WebRequestMethods.Ftp class are the only supported options for the Method property. Setting the Method property to any other value will result in an ArgumentException exception.
SITE and SYST are not among the predefined options, so I guess you're stuck...
Don't waste time to try the WebClient class, it will give you even less flexibility than FtpWebRequest.
However, there are plenty of third-party FTP implementation, open source or commercial, and I'm pretty sure some of them can handle custom commands...
The FtpWebRequest won't help you as Thomas Levesque has said in his answer. You can use some third party solutions or the following, simplified TcpClient based code which I have refactored from an answer written in Visual Basic:
public static void SendFtpCommand()
{
var serverName = "[FTP_SERVER_NAME]";
var port = 21;
var userName = "[FTP_USER_NAME]";
var password = "[FTP_PASSWORD]"
var command = "SITE CHMOD 755 [FTP_FILE_PATH]";
var tcpClient = new TcpClient();
try
{
tcpClient.Connect(serverName, port);
Flush(tcpClient);
var response = TransmitCommand(tcpClient, "user " + userName);
if (response.IndexOf("331", StringComparison.OrdinalIgnoreCase) < 0)
throw new Exception(string.Format("Error \"{0}\" while sending user name \"{1}\".", response, userName));
response = TransmitCommand(tcpClient, "pass " + password);
if (response.IndexOf("230", StringComparison.OrdinalIgnoreCase) < 0)
throw new Exception(string.Format("Error \"{0}\" while sending password.", response));
response = TransmitCommand(tcpClient, command);
if (response.IndexOf("200", StringComparison.OrdinalIgnoreCase) < 0)
throw new Exception(string.Format("Error \"{0}\" while sending command \"{1}\".", response, command));
}
finally
{
if (tcpClient.Connected)
tcpClient.Close();
}
}
private static string TransmitCommand(TcpClient tcpClient, string cmd)
{
var networkStream = tcpClient.GetStream();
if (!networkStream.CanWrite || !networkStream.CanRead)
return string.Empty;
var sendBytes = Encoding.ASCII.GetBytes(cmd + "\r\n");
networkStream.Write(sendBytes, 0, sendBytes.Length);
var streamReader = new StreamReader(networkStream);
return streamReader.ReadLine();
}
private static string Flush(TcpClient tcpClient)
{
try
{
var networkStream = tcpClient.GetStream();
if (!networkStream.CanWrite || !networkStream.CanRead)
return string.Empty;
var receiveBytes = new byte[tcpClient.ReceiveBufferSize];
networkStream.ReadTimeout = 10000;
networkStream.Read(receiveBytes, 0, tcpClient.ReceiveBufferSize);
return Encoding.ASCII.GetString(receiveBytes);
}
catch
{
// Ignore all irrelevant exceptions
}
return string.Empty;
}
You can expect the following flow while getting through the FTP:
220 (vsFTPd 2.2.2)
user [FTP_USER_NAME]
331 Please specify the password.
pass [FTP_PASSWORD]
230 Login successful.
SITE CHMOD 755 [FTP_FILE_PATH]
200 SITE CHMOD command ok.
You can try our Rebex FTP component:
// create client and connect
Ftp client = new Ftp();
client.Connect("ftp.example.org");
client.Login("username", "password");
// send SITE command
// note that QUOTE and SITE are ommited. QUOTE is command line ftp syntax only.
client.Site("LRECL=132 RECFM=FB");
// send SYST command
client.SendCommand("SYST");
FtpResponse response = client.ReadResponse();
if (response.Group != 2)
; // handle error
// disconnect
client.Disconnect();
Use sendCommand("SITE LRECL=242 BLKSIZE=0 RECFM=FB");

Categories

Resources