C# Upload whole directory using FTP - c#

What I'm trying to do is to upload a website using FTP in C# (C Sharp). So I need to upload all files and folders within a folder, keeping its structure. I'm using this FTP class: http://www.codeproject.com/Tips/443588/Simple-Csharp-FTP-Class for the actual uploading.
I have come to the conclusion that I need to write a recursive method that goes through every sub-directory of the main directory and upload all files and folders in it. This should make an exact copy of my folder to the FTP. Problem is... I have no clue how to write a method like that. I have written recursive methods before but I'm new to the FTP part.
This is what I have so far:
private void recursiveDirectory(string directoryPath)
{
string[] filePaths = null;
string[] subDirectories = null;
filePaths = Directory.GetFiles(directoryPath, "*.*");
subDirectories = Directory.GetDirectories(directoryPath);
if (filePaths != null && subDirectories != null)
{
foreach (string directory in subDirectories)
{
ftpClient.createDirectory(directory);
}
foreach (string file in filePaths)
{
ftpClient.upload(Path.GetDirectoryName(directoryPath), file);
}
}
}
But its far from done and I don't know how to continue. I'm sure more than me needs to know this! Thanks in advance :)
Ohh and... It would be nice if it reported its progress too :) (I'm using a progress bar)
EDIT:
It might have been unclear... How do I upload a directory including all sub-directories and files with FTP?

Problem Solved! :)
Alright so I managed to write the method myslef. If anyone need it feel free to copy:
private void recursiveDirectory(string dirPath, string uploadPath)
{
string[] files = Directory.GetFiles(dirPath, "*.*");
string[] subDirs = Directory.GetDirectories(dirPath);
foreach (string file in files)
{
ftpClient.upload(uploadPath + "/" + Path.GetFileName(file), file);
}
foreach (string subDir in subDirs)
{
ftpClient.createDirectory(uploadPath + "/" + Path.GetFileName(subDir));
recursiveDirectory(subDir, uploadPath + "/" + Path.GetFileName(subDir));
}
}
It works very well :)

I wrote an FTP classe and also wrapped it in a WinForms user control. You can see my code in the article An FtpClient Class and WinForm Control.

I wrote a reusable class to upload entire directory to an ftp site on windows server, the program also renames the old version of that folder (i use it to upload my windows service program to the server).
maybe some need this:
class MyFtpClient
{
protected string FtpUser { get; set; }
protected string FtpPass { get; set; }
protected string FtpServerUrl { get; set; }
protected string DirPathToUpload { get; set; }
protected string BaseDirectory { get; set; }
public MyFtpClient(string ftpuser, string ftppass, string ftpserverurl, string dirpathtoupload)
{
this.FtpPass = ftppass;
this.FtpUser = ftpuser;
this.FtpServerUrl = ftpserverurl;
this.DirPathToUpload = dirpathtoupload;
var spllitedpath = dirpathtoupload.Split('\\').ToArray();
// last index must be the "base" directory on the server
this.BaseDirectory = spllitedpath[spllitedpath.Length - 1];
}
public void UploadDirectory()
{
// rename the old folder version (if exist)
RenameDir(BaseDirectory);
// create a parent folder on server
CreateDir(BaseDirectory);
// upload the files in the most external directory of the path
UploadAllFolderFiles(DirPathToUpload, BaseDirectory);
// loop trough all files in subdirectories
foreach (string dirPath in Directory.GetDirectories(DirPathToUpload, "*",
SearchOption.AllDirectories))
{
// create the folder
CreateDir(dirPath.Substring(dirPath.IndexOf(BaseDirectory), dirPath.Length - dirPath.IndexOf(BaseDirectory)));
Console.WriteLine(dirPath.Substring(dirPath.IndexOf(BaseDirectory), dirPath.Length - dirPath.IndexOf(BaseDirectory)));
UploadAllFolderFiles(dirPath, dirPath.Substring(dirPath.IndexOf(BaseDirectory), dirPath.Length - dirPath.IndexOf(BaseDirectory))
}
}
private void UploadAllFolderFiles(string localpath, string remotepath)
{
string[] files = Directory.GetFiles(localpath);
// get only the filenames and concat to remote path
foreach (string file in files)
{
// full remote path
var fullremotepath = remotepath + "\\" + Path.GetFileName(file);
// local path
var fulllocalpath = Path.GetFullPath(file);
// upload to server
Upload(fulllocalpath, fullremotepath);
}
}
public bool CreateDir(string dirname)
{
try
{
WebRequest request = WebRequest.Create("ftp://" + FtpServerUrl + "/" + dirname);
request.Method = WebRequestMethods.Ftp.MakeDirectory;
request.Proxy = new WebProxy();
request.Credentials = new NetworkCredential(FtpUser, FtpPass);
using (var resp = (FtpWebResponse)request.GetResponse())
{
if (resp.StatusCode == FtpStatusCode.PathnameCreated)
{
return true;
}
else
{
return false;
}
}
}
catch
{
return false;
}
}
public void Upload(string filepath, string targetpath)
{
using (WebClient client = new WebClient())
{
client.Credentials = new NetworkCredential(FtpUser, FtpPass);
client.Proxy = null;
var fixedpath = targetpath.Replace(#"\", "/");
client.UploadFile("ftp://" + FtpServerUrl + "/" + fixedpath, WebRequestMethods.Ftp.UploadFile, filepath);
}
}
public bool RenameDir(string dirname)
{
var path = "ftp://" + FtpServerUrl + "/" + dirname;
string serverUri = path;
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(serverUri);
request.Method = WebRequestMethods.Ftp.Rename;
request.Proxy = null;
request.Credentials = new NetworkCredential(FtpUser, FtpPass);
// change the name of the old folder the old folder
request.RenameTo = DateTime.Now.ToString("yyyyMMddHHmmss");
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
using (var resp = (FtpWebResponse)request.GetResponse())
{
if (resp.StatusCode == FtpStatusCode.FileActionOK)
{
return true;
}
else
{
return false;
}
}
}
catch
{
return false;
}
}
}
Create an instance of that class:
static void Main(string[] args)
{
MyFtpClientftp = new MyFtpClient(ftpuser, ftppass, ftpServerUrl, #"C:\Users\xxxxxxxxxxx");
ftp.UploadDirectory();
Console.WriteLine("DONE");
Console.ReadLine();
}

Unless you're doing this for fun or self-improvement, use a commercial module. I can recommend one from Chilkat, but I'm sure there are others.
Note: I'm pretty sure this does answer the stated problem, What I'm trying to do is to upload a website using FTP in C# (C Sharp).

Related

(Updated) Working with files, check if exists or not

I am working with files on C# and I got to a point where I don't know how to continue anymore.
The scenario is this: If I upload 3 or more files with the same name at the same time, I want to handle them and change their name to from "myfile.pdf" to "myfile.pdf(1)/(2)/(3)..." depending on how much files I upload.
This is what I have tried so far and in this case, this only works for only the second file because when the third one comes, it will check there is any file with the same - yes, okay name it "myfile.pdf(2) - but this exists too so it will go to another place.
How can I achieve having the same three files in the same folder with this naming convention?
Here's what I have tried so far:
string FileName = "MyFile.pdf";
string path = #"C:\Project\MyPdfFiles\"
if (File.Exists(path))
{
int i = 1;
var FileExists = false;
while (FileExists==false)
{
if (FileExists == false)
{
FileName = FileName + "(" + i + ")";
}
else
return;
i++;
}
}
And the result of this code is: "MyFile.pdf", "MyFile.pdf(1)" And the third one doesn't load here.
I think I'm missing something in the loop or idk :(.
Can someone help me?
I have tried also this:
if(File.Exists(path) || File.Exists(path+"(")
//because when the second file its uploaded, its name will be SecondFile.pdf(1), so this will return true and will proceed running, but still the iteration will "always" start from 0 since everytime I upload a file, I have to refresh the process.
Don't use return inside your while loop, better set 'FileExists = true' whenever you want you loop to stop. A return statement will exit your current method.
I think your problem can be easily solved using recursion, something like this (untested):
public class Program
{
public string FileName { get; set; }
public Program() {
string fileName = "MyFile.pdf";
string path = #"C:\Project\MyPdfFiles\";
FileName = CheckFileName(path, fileName);
}
public string CheckFileName(string path, string fileName, int iteration = 0) {
if (File.Exists($"{path}{fileName}")) {
iteration++;
CheckFileName(path, $"{fileName}({iteration})", iteration);
}
return fileName;
}
}
What this does is: it CheckFileName method will keep calling itself until it finds a name that doesn't exist yet.
This should do the job.
public class Program
{
public static string GetUnusedFilePath(string directorypath, string filename, string ext)
{
string fullPath = $"{directorypath}{filename}{ext}";
int inc = 0;
// check until you have a filepath that doesn't exist
while (File.Exists(fullPath))
{
fullPath = $"{directorypath}{filename}{inc}{ext}";
inc++;
}
return fullPath;
}
public static void UploadFile(string filepath)
{
using (FileStream fs = File.Create(filepath))
{
// Add some text to file
Byte[] title = new UTF8Encoding(true).GetBytes("New Text File");
fs.Write(title, 0, title.Length);
}
}
public static void Main()
{
string[] filestoUpload = { "file", "file", "file", "anotherfile", "anotherfile", "anotherfile" };
string directorypath = #"D:\temp\";
string ext = ".txt";
foreach(var file in filestoUpload)
{
var filePath = GetUnusedFilePath(directorypath, file, ext);
UploadFile(filePath);
}
}
}
I solved this by creating new folders with special names using the code below:
DirectoryInfo hdDirectoryInWhichToSearch = new DirectoryInfo(FileDirectory);
FileSystemInfo[] filesAndDirs = hdDirectoryInWhichToSearch.GetFileSystemInfos("*" + FullFileName + "*");
int i = filesAndDirs.Length;
if (i>1)
{
FileName = Filename + "(" + i ")";
}
So what this does is that it will count how many files we have in that folder with the same name, so I have to check if we have more than 1 file, then change it's name to file(1).
Thank you to everyone that tried to help me, much appreciated.

Core mvc: Files not getting saved in wwwroot folder but creating files with "wwwroot.....jpg" name in the root

The image uploading is working perfectly in the development server in the windows environment but when I run the code in the Remote Linux server, the files get uploaded but in the root folder and for which the files can not be accessed by the website.
public async Task<IActionResult> Index(IList<IFormFile> files,Type type)
{
Startup.Progress = 0;
foreach (IFormFile source in files)
{
if (isFileImage(source))
{
string filename = ContentDispositionHeaderValue.Parse(source.ContentDisposition).FileName.ToString().Trim('"');
filename = this.EnsureCorrectFilename(filename);
string serverFilePath = this.GetPathAndFilename(filename);
try
{
await source.CopyToAsync(new FileStream(serverFilePath,FileMode.Create));
}
catch (Exception e)
{
}
finally
{
}
}
}
return Content("Success");
}
private string GetPathAndFilename(string filename)
{
string path = Path.Combine(Directory.GetCurrentDirectory(),#"wwwroot\images\materials", filename);
return path;
}
This is the code responsible for uploading an image. In the development windows environment, it works perfectly as the files get saved in the "wwwroot\images\materials" folder.
But when the code is run the Remote Linux serves the files get uploaded but are saved in the root folder with "wwwroot\images\materials*.jpg" name. Even when running the code in development mode in the Remote server this problem occurs.
Since you're using Path.Combine I would suggest passing each part of the path as a parameter. So instead of #"wwwroot\images\materials" as one parameter, you would pass them separately "wwwroot", "images", "materials".
Try this simple. In this you have to inject _hostingEnvironment so you can get ContentRootPath
string folderName = "Upload/Profile/" + user.Id;
string webRootPath = _hostingEnvironment.ContentRootPath;
string newPath = Path.Combine(webRootPath, folderName);
if (!Directory.Exists(newPath))
{
Directory.CreateDirectory(newPath);
}
string extention = file.ContentType.Split("/")[1];
string fileName = user.Id + ".jpg";
string fullPath = Path.Combine(newPath, fileName);
string envpath = folderName + "/" + fileName;
using (var stream = new FileStream(fullPath, FileMode.Create))
{
file.CopyTo(stream);
}

Upload file to folder or subfolder on Sharepoint

Im trying to create a method to upload a file stream to a sharepoint so far i have this
public static void SPUploadFileStream(string username, string filePath, Stream fileData)
{
//string siteUrl = Configuration.SPSiteURL;
string siteUrl = SPContext.Current.Web.Url;
SPUser currentUser = SPUtils.GetCurrentUser(username);
if (currentUser == null)
{
throw new SPGappUnknownUserException(username);
}
using (SPSite site = new SPSite(siteUrl, currentUser.UserToken))
{
using (SPWeb web = site.OpenWeb())
{
bool allowWebUnsafeUpdt = web.AllowUnsafeUpdates;
if (!allowWebUnsafeUpdt)
web.AllowUnsafeUpdates = true;
try
{
SPCreateFolder(Path.GetDirectoryName(filePath), username);
SPFile newFile = web.Files.Add(filePath, fileData, true); //true = replace
}
catch (Exception ex)
{
LoggingService.LogError(ex);
//site.AllowUnsafeUpdates = allowSiteUnsefaUpdt;
web.AllowUnsafeUpdates = allowWebUnsafeUpdt;
throw new ApplicationException("ERROR "+ ex.ToString());
}
}
}
}
but it works ok if i have a path like "FOLDER/file.jpg" but it doesn't when i have subfolders "FOLDER/SUB/file.jpg"
can anyone give me some pointers?
My guess is that the problem lies inside your SPCreateFolder method. It should have created folders recursively. As when the you try to add new file with
SPFile newFile = web.Files.Add(filePath, fileData, true); //true = replace
the server relative path must exist. Try following method for folder creation
private static void SPCreateFolder(SPWeb web, string filepath)
{
// since you pass this as Path.GetDictionary it's no longer split by '/'
var foldersTree = filepath.Split('\\');
foldersTree.Aggregate(web.RootFolder, GetOrCreateSPFolder);
}
private static SPFolder GetOrCreateSPFolder(SPFolder sourceFolder, string folderName)
{
SPFolder destination;
try
{
// return the existing SPFolder destination if already exists
destination = sourceFolder.SubFolders[folderName];
}
catch
{
// Create the folder if it can't be found
destination = sourceFolder.SubFolders.Add(folderName);
}
return destination;
}
Then you can execute this with
...
SPCreateFolder(web, Path.GetDirectoryName(filePath));
SPFile newFile = web.Files.Add(filePath, fileData, true); //true = replace
...
Let me know if that helps

WebClient downloads files to 0KB?

The files that I need to overwrite are on my local machine. The files that I am retrieving are from my FTP Server. The files are all of the same names, but differ in bytes, e.g., they are updated.
I am using files on my local machine as target files - meaning that I use their names to easily find them on the FTP server.
This is the code that I have written:
private void getFiles () {
string startupPath = Application.StartupPath;
/*
* This finds the files within the users installation folder
*/
string[] files = Directory.GetFiles(startupPath + "\\App_Data", "*.*",
SearchOption.AllDirectories);
foreach (string s in files)
{
/*
* This gets the file name
*/
string fileName = Path.GetFileName(s);
/*
* This gets the folder and subfolders after the main directory
*/
string filePath = s.Substring(s.IndexOf("App_Data"));
downloadFile("user:pass#mysite.tk/updates/App_Data/" + fileName,
startupPath + "\\" + filePath);
}
}
private void downloadFile (string urlAddress, string location)
{
using (WebClient webClient = new WebClient())
{
System.Uri URL = new System.Uri("ftp://" + urlAddress);
webClient.DownloadFileAsync(URL, location);
}
}
After the code finishes, for some reason, the files in the sub-folders are showing as 0KB. This is strange because I know that the every file on my FTP Server is greater than 0KB.
My question is: Why are the files in the subfolders showing as 0KB?
If this post is in anway unclear please let me know and I will do my best to clarify.
In answer to the quesiton in the comments, the following would be one possible way to do this, however it is unclear if getFiles is supposed to be a blocking method or not. In my example I assume it is (the method will not exit until all downloads have completed). I'm not sure about the functionality since I am writing this off the top of my head, but its a general idea.
private void getFiles () {
string startupPath = Application.StartupPath;
/*
* This finds the files within the users installation folder
*/
string[] files = Directory.GetFiles(startupPath + "\\App_Data", "*.*",
SearchOption.AllDirectories);
using (WebClient client = new WebClient())
{
int downloadCount = 0;
client.DownloadDataCompleted +=
new DownloadDataCompletedEventHandler((o, e) =>
{
downloadCount--;
});
foreach (string s in files)
{
/*
* This gets the file name
*/
string fileName = Path.GetFileName(s);
/*
* This gets the folder and subfolders after the main directory
*/
string filePath = s.Substring(s.IndexOf("App_Data"));
downloadFile(client, "user:pass#mysite.tk/updates/App_Data/" + fileName,
startupPath + "\\" + filePath);
downloadCount++;
}
while (downloadCount > 0) { }
}
}
private void downloadFile (WebClient client, string urlAddress, string location)
{
System.Uri URL = new System.Uri("ftp://" + urlAddress);
client.DownloadFileAsync(URL, location);
}
You could use FTPWebRequest for this.
http://msdn.microsoft.com/en-us/library/system.net.ftpwebrequest.aspx

inefficient to efficient way to create subfolder in FTP

I was using Make directory to create a folders and subfolder in my ftp (using filezilla) works fine,but when i try to do in my test server (IIS FTP) doesn't work ,throws 550,file not found or no access.so just a quick way to change the code to create subdirctory in my ftp server works fine but i know its a kinda shitty way to do like that.
Changed my code based on #Markus
var dir = new ConsoleApplication5.Program();
string path = "ftp://1.1.1.1/testsvr01/times/" + "testfile" + "/svr01fileName";
string[] pathsplit = path.ToString().Split('/');
string Firstpath = pathsplit[0] + "/" + pathsplit[1] + "/" + pathsplit[2] + "/" + pathsplit[3] + "/";
string SecondPath = Firstpath + "/" + pathsplit[4] + "/";
string ThirdPath = SecondPath + "/" + pathsplit[5] + "/";
string[] paths = { Firstpath, SecondPath, ThirdPath };
foreach (string pat in paths)
{
bool result= dir.EnsureDirectoryExists(pat);
if (result==true)
{
//do nothing
}
else
{ //create dir
dir.createdir(pat);
}
}
upload(path,filename);
}
private bool EnsureDirectoryExists(string pat)
{
try
{
//call the method the first path is exist ?
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(pat);
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential("sh", "se");
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
return true;
}
}
catch (Exception ex)
{ return false; }
}
public void createdir(string pat)
{
try
{
FtpWebRequest createdir = (FtpWebRequest)FtpWebRequest.Create(new Uri(pat));
createdir.Method = WebRequestMethods.Ftp.MakeDirectory;
createdir.Credentials = new NetworkCredential("sh", "se");
createdir.UsePassive = true;
createdir.UseBinary = true;
createdir.KeepAlive = false;
FtpWebResponse response1 = (FtpWebResponse)createdir.GetResponse();
Stream ftpStream1 = response1.GetResponseStream();
ftpStream1.Close();
response1.Close();
}
catch (Exception ex)
{
}
}
Please if any of you guys find a better way,suggest me.
That's a lot of code for ensuring a directory exists. Is there a Directory.Exists() method (or a similar method you could leverage)? Then you could call an EnsureDirectoryExists() before uploading.
private bool EnsureDirectoryExists(string path)
{
// check if it exists
if (Directory.Exists(path))
return true;
string parentPath = GetParentPath(path);
if (parentPath == null)
return false;
bool result = EnsureDirectoryExists(parentPath);
if (result)
{
CreateDirectory(path);
return true;
}
return false;
}
Disclaimer: You might need to tweak the logic a bit and use the FTP functions, but I hope you get the point.

Categories

Resources