Introduction
If you want to upload some files to your FTP server, you can put these files to the FTP server one by one.
But if the local directory path of these files is different from each other, you should check on the FTP server whether all those directory names exist or not.
Furthermore, if some file's path has many directories, you should create the directories in FTP server step by one. Because you can't create a directory having 2 more steps not existing at once.
It's uncomfortable for me, so, I will make a zip file and put all files on it. ZipArchive offers to create a hierarchy directory by once.
Background
- Drag drop files into a
datagrid
control in WPF project. - When clicking the button which is labeled 'save and upload', the program run creates an XML file (contains files information like path, size, writetime) and makes zip file automatically, and all the files you drag-dropped on it.
- Then, that XML and ZIP file will be uploaded to the FTP server you indicated.
- For this, you should know the relative directory path.
For example, if you add two files in a zip file and they are in different directory paths, you should know a base root path to create an entry folder path in a zip.
First file's real path is d:\abcd\goodjob.txt
Second file's real path is d:\abcd\folder\goodjob1.txt
Then, the local root path must be considered 'd:\abcd\'
So, the first file's relative path is '\goodjob.txt' and the second file's relative path is 'folder\goodjob1.txt'.
- In short, zip file's root has one file and one directory entry called folder and 'goodjob1.txt' file will be in that.
Using the Code
First, you do create a WPF project using .NET 4.6.
The reason why I'm using that version is that ZipArchive
class is offered after .NET 4.5 as I know.
This code below is for 'MainWindow.xaml'.
First of all, you must drag drop files you want to a DataGrid
control named 'dgToUploadFiles
'.
Then, input FTP server URL to the 'Server Root Url'.
If you have to manage different kinds of program version files, you can use the 'Key Directory'. The directory will be created automatically in the FTP server next to 'Server Root Url/SWUpdate/'.
Lastly, you should input the id and password, and click the 'Save and Upload' button.
<Window x:Class="NewSoftwareFileUploader.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="New Software Version Files Uploader"
Height="350" Width="525" x:Name="_this">
<DockPanel LastChildFill="True">
<DockPanel DockPanel.Dock="Top" LastChildFill="True">
<TextBlock Width="100" DockPanel.Dock="Left">Server Root Url:</TextBlock>
<TextBox Name="txtServerRoot" DataContext="{Binding ElementName=_this}"
Text="{Binding Path=ServerRootPath}"></TextBox>
</DockPanel>
<DockPanel DockPanel.Dock="Top" LastChildFill="True">
<TextBlock Width="100" DockPanel.Dock="Left">Key Directory:</TextBlock>
<TextBox Width="100" DockPanel.Dock="Left" Name="txtKeyDir"
DataContext="{Binding ElementName=_this}" Text="{Binding Path=KeyDirectory}"
Margin="0,0,5,0"></TextBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock Width="50" DockPanel.Dock="Left">User ID:</TextBlock>
<TextBox Width="80" DockPanel.Dock="Left" Name="txtUserID"
DataContext="{Binding ElementName=_this}" Text="{Binding Path=UserID}"
Margin="0,0,5,0"></TextBox>
<TextBlock Width="60" DockPanel.Dock="Left">Password:</TextBlock>
<PasswordBox Width="80" DockPanel.Dock="Left"
Name="txtPassword"></PasswordBox>
</StackPanel>
</DockPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal"
HorizontalAlignment="Right">
<Button Width="50" Height="30" Name="btnDel"
Click="btnDel_Click" Margin="5">Delete</Button>
<Button Width="150" Height="30" Name="btnUpload"
Click="btnSaveAndUpload_Click" Margin="5">Save and Upload</Button>
</StackPanel>
<DockPanel DockPanel.Dock="Bottom" LastChildFill="True">
<TextBlock DockPanel.Dock="Left" Text="Base Relative Path : "></TextBlock>
<TextBlock DataContext="{Binding ElementName=_this}"
Text="{Binding Path=LocalRootPath}"></TextBlock>
</DockPanel>
<DockPanel LastChildFill="True">
<DataGrid Name="dgToUploadFiles" Drop="dgToUploadFiles_Drop"
AutoGenerateColumns="False"
IsReadOnly="True" CanUserDeleteRows="False"
CanUserAddRows="False" SelectionUnit="FullRow" AllowDrop="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Path" Width="200*"
Foreground="Black"
Binding="{Binding FullPathDesc}"></DataGridTextColumn>
<DataGridTextColumn Header="Length" Width="100"
Foreground="Black"
Binding="{Binding LengthDesc}"></DataGridTextColumn>
<DataGridTextColumn Header="LastWriteTime" Width="150"
Foreground="Black"
Binding="{Binding LastWriteTimeDesc}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</DockPanel>
</Window>
And this code below is MainWindow.xaml.cs:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace NewSoftwareFileUploader
{
public partial class MainWindow : Window
{
public static readonly DependencyProperty ServerRootPathProperty =
DependencyProperty.Register("ServerRootPath", typeof(string),
typeof(MainWindow), new UIPropertyMetadata(string.Empty));
public string ServerRootPath
{
get
{
string strTmp = (string)this.GetValue(ServerRootPathProperty);
if (String.IsNullOrEmpty(strTmp)) return "";
if (!strTmp.EndsWith("/")) strTmp += "/";
return strTmp;
}
set
{
string strTmp = value;
if(String.IsNullOrEmpty(strTmp)) return;
if (!strTmp.EndsWith("/")) strTmp += "/";
this.SetValue(ServerRootPathProperty, strTmp);
}
}
public static readonly DependencyProperty KeyDirectoryProperty =
DependencyProperty.Register("KeyDirectory", typeof(string),
typeof(MainWindow), new UIPropertyMetadata(string.Empty));
public string KeyDirectory
{
get
{
string strTmp = (string)this.GetValue(KeyDirectoryProperty);
if (String.IsNullOrEmpty(strTmp)) return "";
if (!strTmp.EndsWith("/")) strTmp += "/";
return strTmp;
}
set
{
string strTmp = value;
if(String.IsNullOrEmpty(strTmp)) return;
if (!strTmp.EndsWith("/")) strTmp += "/";
this.SetValue(KeyDirectoryProperty, strTmp);
}
}
public static readonly DependencyProperty UserIDProperty =
DependencyProperty.Register("UserID", typeof(string),
typeof(MainWindow), new UIPropertyMetadata(string.Empty));
public string UserID
{
get { return (string)this.GetValue(UserIDProperty); }
set { this.SetValue(UserIDProperty, value); }
}
public static readonly DependencyProperty SaveTemporaryPathProperty =
DependencyProperty.Register("SaveTemporaryPath", typeof(string),
typeof(MainWindow), new UIPropertyMetadata(string.Empty));
public string SaveTemporaryPath
{
get
{
string strTmp = (string)this.GetValue(SaveTemporaryPathProperty);
if (String.IsNullOrEmpty(strTmp)) return "";
if (!strTmp.EndsWith("\\")) strTmp += "\\";
return strTmp;
}
set
{
string strTmp = value;
if (String.IsNullOrEmpty(strTmp)) return;
if (!strTmp.EndsWith("\\")) strTmp += "\\";
this.SetValue(SaveTemporaryPathProperty, strTmp);
}
}
public static readonly DependencyProperty LocalRootPathProperty =
DependencyProperty.Register("LocalRootPath", typeof(string),
typeof(MainWindow), new UIPropertyMetadata(string.Empty));
public string LocalRootPath
{
get { return (string)this.GetValue(LocalRootPathProperty); }
set
{
string strTmp = value;
if (String.IsNullOrEmpty(strTmp)) return;
if (!strTmp.EndsWith("\\")) strTmp += "\\";
this.SetValue(LocalRootPathProperty, strTmp);
}
}
public MainWindow()
{
InitializeComponent();
ServerRootPath = "ftp://192.168.0.32/";
KeyDirectory = "SITE1/";
UserID = "young";
SaveTemporaryPath = System.IO.Path.GetDirectoryName
(Process.GetCurrentProcess().MainModule.FileName);
LocalRootPath = "";
}
private void btnConnect_Click(object sender, RoutedEventArgs e)
{
string url = txtServerRoot.Text;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
string html = reader.ReadToEnd();
Regex regex = new Regex("<A HREF=\".*\">(?<name>.*)</A>");
MatchCollection matches = regex.Matches(html);
if (matches.Count > 0)
{
foreach (Match match in matches)
{
if (match.Success)
{
}
}
}
}
}
Console.ReadLine();
}
private void btnDel_Click(object sender, RoutedEventArgs e)
{
if (dgToUploadFiles.SelectedItems.Count <= 0) return;
List<UploadFileInfo> lstRemove = new List<UploadFileInfo>();
lstRemove.AddRange(dgToUploadFiles.SelectedItems.Cast<UploadFileInfo>().ToList());
foreach (UploadFileInfo fileInfo in lstRemove)
dgToUploadFiles.Items.Remove(fileInfo);
}
private String GetXmlNomalizedString(XmlDocument doc, Encoding encoding)
{
try
{
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
IndentChars = " ",
NewLineChars = "\r\n",
NewLineHandling = NewLineHandling.Replace,
Encoding = encoding
};
using (var ms = new MemoryStream())
using (var writer = XmlWriter.Create(ms, settings))
{
try
{
doc.Save(writer);
var xmlString = encoding.GetString(ms.ToArray());
return xmlString;
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
return doc.OuterXml;
}
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
return "";
}
}
private void dgToUploadFiles_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
if (files != null)
{
foreach (String strFullFilePath in files)
{
IEnumerable<UploadFileInfo> existedItem =
dgToUploadFiles.Items.Cast<UploadFileInfo>().Where
(item => item.FullPathDesc == strFullFilePath);
if (existedItem.Count() > 0) continue;
if ((Directory.Exists(strFullFilePath) || File.Exists(strFullFilePath)) &&
!String.IsNullOrEmpty(strFullFilePath))
{
String strDirPath = System.IO.Path.GetDirectoryName(strFullFilePath);
if (!String.IsNullOrEmpty(LocalRootPath))
{
if (!LocalRootPath.StartsWith(strDirPath) &&
!strDirPath.StartsWith(LocalRootPath))
{
MessageBox.Show(
String.Format("[Drop Files] this file '{0}'
is not having the same direction.",
strFullFilePath));
break;
}
else
{
if (strDirPath.Length < LocalRootPath.Length)
LocalRootPath = strDirPath;
}
}
else
LocalRootPath = strDirPath;
FileInfo fs = new FileInfo(strFullFilePath);
if (fs != null)
{
Int64 lLength = fs.Length;
DateTime dtLast = fs.LastWriteTime;
UploadFileInfo fileInfo = new UploadFileInfo
(strFullFilePath, lLength, dtLast);
dgToUploadFiles.Items.Add(fileInfo);
}
}
}
}
}
}
private void btnSaveAndUpload_Click(object sender, RoutedEventArgs e)
{
if(String.IsNullOrEmpty(KeyDirectory))
{
MessageBox.Show("[Save and Archive] input your key directory name");
return;
}
List<UploadFileInfo> lstUploadFiles = new List<UploadFileInfo>();
if (dgToUploadFiles.Items.Count > 0)
{
try
{
XmlDocument xReqDoc = new XmlDocument();
XmlDeclaration xmlDescNode = xReqDoc.CreateXmlDeclaration("1.0", "UTF-8", "yes");
xReqDoc.AppendChild(xmlDescNode);
{
XmlElement rootNode = xReqDoc.CreateElement("", "upload_root", "");
rootNode.SetAttribute("version", "1");
rootNode.SetAttribute("update", DateTime.Now.ToString("yyyyMMddHHmmss"));
{
for (int i = 0; i < dgToUploadFiles.Items.Count; i++)
{
UploadFileInfo fileInfo = dgToUploadFiles.Items[i] as UploadFileInfo;
if (fileInfo == null) continue;
String strFullFilePath = fileInfo.FullPathDesc;
if (!String.IsNullOrEmpty(strFullFilePath))
{
FileInfo fs = new FileInfo(strFullFilePath);
if (fs != null)
{
Int64 lLength = fs.Length;
DateTime dtLast = fs.LastWriteTime;
fileInfo.CreateXml(ref xReqDoc, ref rootNode, LocalRootPath);
lstUploadFiles.Add(fileInfo);
}
}
}
}
xReqDoc.AppendChild(rootNode);
}
String strXmlData = GetXmlNomalizedString(xReqDoc, Encoding.UTF8);
String strTmpDir = String.Format(
"{0}UpdateTemporary\\{1}\\",
SaveTemporaryPath.Replace("/", "\\"),
KeyDirectory.Replace("/", "\\"));
if (!Directory.Exists(strTmpDir)) Directory.CreateDirectory(strTmpDir);
String strZipPath = String.Format(
"{0}UpdateArchive.zip",
strTmpDir,
DateTime.Now.ToString("yyyyMMddHHmmss"));
using (FileStream zipToOpen = new FileStream(strZipPath, FileMode.Create))
{
using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Create))
{
foreach (UploadFileInfo fileInfo in lstUploadFiles)
{
archive.CreateEntryFromFile(
fileInfo.FullPathDesc,
fileInfo.RelativePath.Replace(@"\", "/"),
CompressionLevel.Fastest);
}
}
}
if (MessageBox.Show("Do you want to upload this zip file
to ftp server?", "", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Upload(strXmlData, strZipPath);
}
else
{
FileStream fs = new FileStream(
String.Format("{0}UpdateList.xml", strTmpDir),
FileMode.Create,
FileAccess.Write);
using (StreamWriter writer = new StreamWriter(fs, Encoding.UTF8))
{
writer.Write(strXmlData);
}
fs.Close();
fs.Dispose();
Process.Start(strTmpDir);
}
}
catch (Exception ex)
{
MessageBox.Show("[Save and Archive] " + ex.Message);
}
}
else
{
MessageBox.Show("[Save and Archive] there are no files to make archive");
}
}
private bool IsExistedFolderInFTP(String strServerPath, out String strErr)
{
strErr = "";
FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create(strServerPath);
ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
ftpRequest.Credentials = new NetworkCredential(UserID, txtPassword.Password);
try
{
using (var result = (FtpWebResponse)ftpRequest.GetResponse())
{
result.Close();
return true;
}
}
catch (WebException e)
{
FtpWebResponse response = (FtpWebResponse)e.Response;
if (response.StatusCode != FtpStatusCode.ActionNotTakenFileUnavailable)
{
strErr = e.ToString();
}
}
return false;
}
private bool MakeFolderInFTP(String strServerPath, out String strErr)
{
strErr = "";
FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create(strServerPath);
ftpRequest.Method = WebRequestMethods.Ftp.MakeDirectory;
ftpRequest.Credentials = new NetworkCredential(UserID, txtPassword.Password);
try
{
using (var result = (FtpWebResponse)ftpRequest.GetResponse())
{
result.Close();
return true;
}
}
catch (WebException e)
{
strErr = e.ToString();
}
return false;
}
private bool UpdateXmlFileData(String strServerPath, string strXmlData, out String strErr)
{
strErr = "";
string updateFileURL = String.Format("{0}UpdateList.xml", strServerPath);
try
{
Uri uri = new Uri(updateFileURL);
FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create(updateFileURL);
ftpRequest.Method = WebRequestMethods.Ftp.UploadFile;
ftpRequest.Credentials = new NetworkCredential(UserID, txtPassword.Password);
ftpRequest.Proxy = null;
ftpRequest.UsePassive = false;
ftpRequest.UseBinary = true;
ftpRequest.KeepAlive = false;
Thread.Sleep(200);
Stream requestStream = ftpRequest.GetRequestStream();
byte[] buffer = new byte[2048];
byte[] byteXmlData = Encoding.UTF8.GetBytes(strXmlData);
requestStream.Write(byteXmlData, 0, byteXmlData.Length);
requestStream.Close();
FtpWebResponse uploadResponse = (FtpWebResponse)ftpRequest.GetResponse();
if (uploadResponse != null) uploadResponse.Close();
if (requestStream != null) requestStream.Close();
return true;
}
catch (Exception e)
{
strErr = e.ToString();
}
return false;
}
public void Upload(string strXmlData, string uploadfile)
{
if(String.IsNullOrEmpty(UserID) || String.IsNullOrEmpty(txtPassword.Password))
{
MessageBox.Show("[Upload] " + "you must input your id and password");
return;
}
String strErr;
string[] aryToMakeDirs = KeyDirectory.Replace('\\', '/').Split
(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
string strServerPath = ServerRootPath;
foreach(String strToMakeDir in aryToMakeDirs)
{
strServerPath += strToMakeDir + "/";
bool isExisted = IsExistedFolderInFTP(strServerPath, out strErr);
if (!isExisted && String.IsNullOrEmpty(strErr))
{
if (!MakeFolderInFTP(strServerPath, out strErr))
{
MessageBox.Show("[Upload] " + strErr);
return;
}
}
else if (!isExisted)
{
MessageBox.Show("[Upload] " + strErr);
return;
}
}
if(UpdateXmlFileData(strServerPath, strXmlData, out strErr))
{
string updateFileURL = String.Format(
"{0}{1}",
strServerPath,
System.IO.Path.GetFileName(uploadfile));
try
{
Uri uri = new Uri(updateFileURL);
FileInfo fInfo = new FileInfo(uploadfile);
if (fInfo.Exists)
{
FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create(updateFileURL);
ftpRequest.Method = WebRequestMethods.Ftp.UploadFile;
ftpRequest.Credentials = new NetworkCredential(UserID, txtPassword.Password);
ftpRequest.Proxy = null;
ftpRequest.UsePassive = false;
ftpRequest.UseBinary = true;
ftpRequest.KeepAlive = false;
Thread.Sleep(200);
Stream requestStream = ftpRequest.GetRequestStream();
FileStream resultFileStream = new FileStream(uploadfile, FileMode.Open);
byte[] buffer = new byte[2048];
Int64 lngTotalFileDataBlock = fInfo.Length;
int bytesRead;
while (true)
{
bytesRead = resultFileStream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
break;
}
requestStream.Write(buffer, 0, bytesRead);
}
requestStream.Close();
FtpWebResponse uploadResponse = (FtpWebResponse)ftpRequest.GetResponse();
if (uploadResponse != null) uploadResponse.Close();
if (resultFileStream != null) resultFileStream.Close();
if (requestStream != null) requestStream.Close();
MessageBox.Show("[Upload] success");
}
else
{
MessageBox.Show("[Upload] zip file is not existed");
}
}
catch (Exception e)
{
MessageBox.Show("[Upload] " + e.ToString());
}
}
else
{
MessageBox.Show("[Upload] " + strErr);
}
}
}
}
If you follow me, you will know that you must create 'UploadFileInfo
' class. It is right under here.
This class is one that has its file information. When creating XML file, it will generate its information to XML string
on its own.
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
namespace NewSoftwareFileUploader
{
public class UploadFileInfo
{
private string strFullPath;
public String FullPathDesc { get { return strFullPath; } }
public string RelativePath { get; set; }
private long lLength;
public String LengthDesc { get { return lLength.ToString(); } }
private DateTime dtLastWriteTime;
public String LastWriteTimeDesc { get { return dtLastWriteTime.ToString(); } }
private UploadFileInfo()
{
strFullPath = "";
RelativePath = "";
lLength = -1;
dtLastWriteTime = new DateTime(0);
}
public UploadFileInfo(string path, long len, DateTime dt)
{
strFullPath = path;
lLength = len;
dtLastWriteTime = dt;
}
public void CreateXml(ref XmlDocument xReqDoc, ref XmlElement rootNode, String strRootPath)
{
XmlElement fileNode = xReqDoc.CreateElement("", "file", "");
{
XmlElement pathNode = xReqDoc.CreateElement("", "path", "");
{
RelativePath = FullPathDesc.Replace(strRootPath, "");
pathNode.InnerText = RelativePath;
}
fileNode.AppendChild(pathNode);
XmlElement sizeNode = xReqDoc.CreateElement("", "size", "");
{
sizeNode.InnerText = LengthDesc;
}
fileNode.AppendChild(sizeNode);
XmlElement datetimeNode = xReqDoc.CreateElement("", "datetime", "");
{
datetimeNode.InnerText = LastWriteTimeDesc;
}
fileNode.AppendChild(datetimeNode);
}
rootNode.AppendChild(fileNode);
}
}
}
Points of Interest
The reason why I'm creating file information to XML file is because it is easy to read from the web server.
After setting web server by IIS, if your FTP server URL and web server URL located to the same location. You can use the 'WebClient
' which is offered by .NET.
History