Работа с FTP сервером из C#
Язык программирования C#Наверное многие сталкивались с необходимостью создать простой FTP клиент для своих нужд на C#. Вот и меня не миновало. Недавно пришлось делать специфическое приложение для обновления ПО через FTP. И самым полезным что я нашёл для этой цели оказался набор библиотечек (кстати с исходниками) BytesRoad.NetSuit. Очень простой компонент предоставляющий весь основной функционал для работы с FTP.
В коплекте 3 библиотеки:
BytesRoad.Diag.dll
BytesRoad.Net.Ftp.dll
BytesRoad.Net.Sockets.dll
Добавляем в наш проект как References вот эти две библиотеки:
BytesRoad.Net.Ftp.dll
BytesRoad.Net.Sockets.dll
Далее о том как работать с компонентом…
Устанавливаем соединение с сервером:
//Сам клиент ФТП
FtpClient client = new FtpClient();
//Задаём параметры клиента.
client.PassiveMode = true; //Включаем пассивный режим.
int TimeoutFTP = 30000; //Таймаут.
string FTP_SERVER = "адрес фтп сервера";
int FTP_PORT = "порт ФТП сервера";
string FTP_USER = "пользователь";
string FTP_PASSWORD = "пароль";
//Если используется прокси сервер то можем задать параметры прокси.
FtpProxyInfo pinfo = new FtpProxyInfo(); //Это переменная параметров.
pinfo.Server = "192.168.0.202";
pinfo.Port = 21; //Порт.
pinfo.Type = FtpProxyType.HttpConnect; //Тип прокси - всего 4 вида.
pinfo.PreAuthenticate = true; //Если на прокси есть идентификация
pinfo.User = "Имя пользователя";
pinfo.Password = "Пароль пользователя";
//Присваиваем параметры прокси клиенту.
client.ProxyInfo = pinfo;
//Подключаемся к FTP серверу.
client.Connect(TimeoutFTP, FTP_SERVER, FTP_PORT);
client.Login(TimeoutFTP, FTP_USER, FTP_PASSWORD);
//
//.... Здесь выполняем то что хотим с сервером ...
//
//Отключаемся от ФТП сервера
client.Disconnect(TimeoutFTP);
Теперь немного о самых основных методах для работы с FTP сервером:
//Получает список содержимого текущего каталога с FTP.
client.GetDirectoryList(TimeoutFTP);
//Меняет директорию на указанную.
//Можно переходить вверх указав вместо имени папки ".." либо в любую папку расположенную в текущей.
client.ChangeDirectory(TimeoutFTP, "папка");
//Удаляет указанный файл с сервера.
client.DeleteFile(TimeoutFTP, "файл");
//Удаляет указанную папку с сервера.
client.DeleteDirectory(TimeoutFTP, "файл");
//Принимает указанный файл с сервера.
client.GetFile(TimeoutFTP, "куда принимаем - путь на диске", "Что принимаем - файл на сервере");
//загружаем файл на сервер.
client.PutFile(TimeoutFTP, "имя файла на сервере", "что грузим - имя файла на компьютере");
Как уже полагаю заметили, довольно неудобно что надо каждый раз указывать таймаут.
Но впринципе с этим можно смириться, остальное то всё работает.
Я бы как обычно привёл ссылочку на сайт разработчика где можно скачать компонент, но похоже сайт разработчика не пережил кризиса :). Поэтому отступлю от своего правила, библиотеку можно взять здесь: BytesRoad.NetSuit_2_0.zip.
Также я написал маленький примерчик работы с этим компонентом,(загрузка удаление и получение файлов в примере реализоавны по правой кнопке в выпадающем меню).
Вот скриншоты:


А вот сам исходник: BytesRoadFtpExample.rar.
Комментарии:
Вот подключаемся к серверу с теме настройками что у тебя и выбираем файл и хотим сохранить его на диске D:\ и получаем такую мессагу.
Действительно, сейчас проверил, ошибку выдаёт такую. Завтра гляну — по всей видимости что-то где-то не так сделал. Потому-что в проекте, который на ней делал всё точно работало и в нужные папочки сохраняло.
ОтветитьВсё оказалось довольно просто, ступил маленько.
Там надо при приёме файла, вместо этой строчки:
client.GetFile(MainParams.FtpTimeout * 1000, folderBrowserDialog1.SelectedPath, treeView1.SelectedNode.Text);
Написать вот эту:
client.GetFile(MainParams.FtpTimeout * 1000, folderBrowserDialog1.SelectedPath+treeView1.SelectedNode.Text, treeView1.SelectedNode.Text);
Просто вторым параметром должна идти не папка, а весь путь включая имя файла. Вот он и ругался. :) Ответить
Здравствуйте, а можно как то с помощью функции
client.GetFile(TimeoutFTP, «куда принимаем — путь на диске», «Что принимаем — файл на сервере»);
скачать файлы определённого расширения, например, файлы .rar, .zip, .arj, .doc ???
Конечно можно.
Примерно так:
foreach (FtpItem item in client.GetDirectoryList(таймаут))//Перебираем итемы в папке
{
if (item.ItemType == FtpItemType.File)
{
//Если итем - является файлом (помимо файла могут быть ссылкой и директорией),
// то здесь проверяем его расширение,
//И загружаем если необходимо по условию.
}
}
Ответить
Здравствуйте, спасибо за ответ, но я так и делаю. Просто я думал вы мне подскажите какой-нибудь способ в C# проверки файла по расширению.
ОтветитьА как получить размер файла?
ОтветитьВот так:
FtpItem item = new FtpItem();
long razmer = item.Size;
Ответить
Ой, а можно на примере показать как узнать размер определенного файла на фтп сервере?
Что передавать в FtpItem()? Попробовал имя файла, возвращает всегда "-1"
Вот измененная процедура получения списка файлов с ФТП из примера:
//Получение списка файлов текущего каталога с ФТП
private void GetItemsFromFtp()
{
foreach (FtpItem item in client.GetDirectoryList(MainParams.FtpTimeout * 1000))
{
TreeNode node = new TreeNode(item.Name + ": " + item.Size.ToString()+" Bytes");
node.ImageIndex = 2;
node.SelectedImageIndex = 2;
node.Tag = 0;
if (item.ItemType == FtpItemType.Directory)
{
node.ImageIndex = 0;
node.SelectedImageIndex = 0;
node.Tag = 1;
}
if (item.ItemType == FtpItemType.File)
{
node.ImageIndex = 1;
node.SelectedImageIndex = 1;
}
if (item.ItemType == FtpItemType.Link)
{
node.ImageIndex = 3;
node.SelectedImageIndex = 3;
}
treeView1.Nodes.Add(node);
}
}
Вот весь измененный пример целиком: BytesRoadFtpExample_Size.rar Ответить
А может и скорость скачки можно регулировать? Как-то уж оно медленно тянет
ОтветитьВот, это уже средствами самой библиотеки вряд-ли.
Сервер ведь отдает данные с определённым ограничением по скорости, и тут уже единственное что можно сделать (в случае если свой интернет канал позволяет) это разбить скачку на несколько потоков ( и еще если сервер позволит в несколько потоков к нему подключиться).
Самое элементарное и наиболее простое в реализации, на мой взгляд, это если надо тянуть много файлов — тянуть разные файлы в несколько подключений. Каждый файл в своем потоке.
Пытался написать процедуру для поднятия вверх по каталогу
private void вверхToolStripMenuItem_Click(object sender, EventArgs e)
{
client.ChangeDirectory(MainParams.FtpTimeout, "..");
}
Выбрасывает в тайм-аут :( Ответить
Если делалось прямо в исходнике пример то надо вот так делать:
private void вверхToolStripMenuItem_Click(object sender, EventArgs e)
{
client.ChangeDirectory(MainParams.FtpTimeout * 1000, "..");
treeView1.Nodes.Clear();
GetItemsFromFtp();
}
Там таймаут — он задается в параметрах в секундах (так более юзер-френдли), а нужен в миллисекундах. Поэтому умножаем на 1000, не успевает он за 30 миллисекунд.
Только что проверил, работает 100%. Ответить
Спасибо всё работает, забыл про ноды и что в исходнике не было в начале умножения на 1000, ваяю дальше!
ОтветитьПодскажите пожалуйста способ прикрутить ProgressBar к upload ну или к download.
ОтветитьЯ делал примерно так:
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
string[] name = treeView1.SelectedNode.Text.Split(':');
string size = name[1].Remove(name[1].Length-5,5);
size = size.Trim();
long sz_all = long.Parse(size);
long sz = 0;
while (sz < sz_all)
{
client.GetFile(MainParams.FtpTimeout * 1000, name[0], name[0], sz, 100000);
//Источник
FileStream source = new FileStream(name[0], FileMode.Open, FileAccess.Read, FileShare.Read);
//Приемник
FileStream dest = new FileStream(folderBrowserDialog1.SelectedPath + "\\" + name[0], FileMode.Append, FileAccess.Write, FileShare.None);
Byte[] buffer = new Byte[32];
while (source.Position != source.Length)
{
int n = source.Read(buffer, 0, buffer.Length);
dest.Write(buffer, 0, n);
}
dest.Flush();
source.Close();
dest.Close();
sz = sz + 100000;
this.Text = "Скачано: " + (sz / 1000).ToString() + " Kb.";
Application.DoEvents();
}
}
Здесь скачиваются кусочки файла по 100 килобайт и сливаются в один. Но это работает медленно. Думаю каждый кусочек он ищет на сервере по новой — поэтому и долго.
Была идея принимая файл из него информацию сливать в окончательный файл, но мне не понадобилось. В принципе это примерно то-же что и код выше, только быстрее.
Думаю в компоненте есть каике-то механизмы более продвинутые для этих целей, но с ними не разбирался.
Я для своих нужд обычно просто получаю список файлов, и по их количеству ProgressBar — заполняю. Это самый простой надежный и быстрый вариант, на мой взгляд. Ответить
Большое спасибо с закачкой все понятно… А вот с Аплоадом ума не приложу =(
Заранее Благодарю…
Есть более простой способ — подключить событие DataTransfered и обрабатывать его.
Dim ftpclient As New BytesRoad.Net.Ftp.FtpClient
ftpclient.PassiveMode = True
AddHandler ftpclient.DataTransfered, AddressOf data_transfered
//Выполняем всё необходимое
//Я скачивал все файлы из директории (Progressbar1 - обновлялся по количеству файлов, а ProgressBar2 - отображал процесс текущего файла
Dim dirlist() As BytesRoad.Net.Ftp.FtpItem = ftpclient.GetDirectoryList(FTP_TIMEOUT)
ProgressBar1.Maximum = dirlist.lenght
Dim cnt As Integer = 0
For Each itm As BytesRoad.Net.Ftp.FtpItem In dirlist
Select Case itm.ItemType
Case BytesRoad.Net.Ftp.FtpItemType.File
ProgressBar2.Value = 0
ProgressBar2.Maximum = itm.Size
ftpclient.GetFile(FTP_TIMEOUT, "d:\_ftp\" + itm.Name, itm.Name)
ProgressBar1.Value = cnt + 1
ProgressBar1.Update()
Application.DoEvents()
cnt += 1
End Select
Next
//Обработчик события.
Private Sub data_transfered(ByVal sender As Object, ByVal e As BytesRoad.Net.Ftp.DataTransferedEventArgs)
ProgressBar2.Value += e.LastTransfered
Application.DoEvents()
End Sub
Я думаю, адаптировать код к C# не составит трудов. Ответить
Уважаемые специалисты, никто не сталкивался с проблемой при «Подключении к серверу»?
Ввел следующие параметры
И в ответ получаю окно сообщения.
и сразу после него вот такое
Есть у кого-нибудь идеи?
На ум не приходит ничего, кроме проблем с авторизацией через прокси.
ftp.microsoft.com — точно пускает при таких параметрах.
Может при анаонимном подключении к прокси тоже надо какого нибудь пользователя anonymous — указывать?
Я для сравнения пробую в TotalCommander те же настройки — то есть
host — ftp.microsoft.com
user — anonymous
password — ничего не пишу
использовать прокси proxy.city-yar.ru:3128 (HTTP с поддержкой FTP)
Тут тоже можно ввести логин / пароль учетной записи, но я этого не делаю.
И все нормально — заходит. Мои скрины ведь соответствуют такому входу?
Да соответствуют.
Даже не знаю, потыкал сам, на нескольких анонимных прокси в интернете, не входит действительно. Не знаю даже, мне как-то через прокси никогда не надо было, я особо и не тестировал.
Ооооочень жаль… Ну просто очень. Proxy — это, как я считаю — очень важный функционал при работе с FTP. Я надеялся, что это я не то что-то вписываю…
В коде есть метод ConnectFTP(). В нем строка
client.Connect(MainParams.FtpTimeout * 1000, MainParams.FtpServer, MainParams.FtpPort);
Так вот на ней выбрасывается исключение.
Я, кстати, встречал на форумах запись, что внутри библиотеки которая здесь используется (вроде вот в этой BytesRoad.Net.Ftp.dll) выбрасывается исключение при конекте с proxy… То есть вот то, что я читаю в MessageBox после конекта с Proxy — это уже обработанное сообщение из библиотеки.
Хочу сделать прогу которая периодически проверяет наличие файла на ФТП сервере и его скачивает. Использую
client.GetFile(MainParams.FtpTimeout * 1000, MainParams.Path + MainParams.GetFile, MainParams.GetFile);
Файл скачивается. проблем нет. Проблема в том что если запустить эту часть кода когда файл отсутствует на ФТП. В локальной папке на компе просто появляется файл. Пустое файл. Как обойти не знаю.
Легко. Просто прежде чем копировать файл получить содержимое нужного каталога с ftp функцией GetDirectoryList проверить есть ли в содержимом то что нам надо, и только если есть копировать.
Например так:
foreach (FtpItem item in client.GetDirectoryList(MainParams.FtpTimeout * 1000))
{
if (item.Name=="нужный.файл")
{
client.GetFile(MainParams.FtpTimeout * 1000, MainParams.Path + MainParams.GetFile, MainParams.GetFile);
}
}
Ответить
Фуг это сущий кошмар… Все таки на php легче, а еще говорят что C# и PHP похожи синтаксисом.
ОтветитьКогда говорят похожи синтаксисом, я так понимаю имеют ввиду похожесть синтаксиса основных основных операторов: for, while, if… else. и т.п.
Было бы странно если бы на PHP работу с FTP было бы сложнее реализовать, это очень близко к основной области его применения.
День добрый!!!
посоветуйте пожалуйста, ситуация такая:
есть ФТП-папка с файлами(только чтение), можно ли узнать с клиента что файл на сервере еще не загружен полностью, как бы сказать клиенту что «файл занят попробуйте скачать его позже»
Большое спасибо…
По файлу даже не знаю, можно ли так сделать — чтобы по самому файлу определить весь он уже или нет, можно лишь определить больше он того что мы уже скачали или нет.
Но вообще в быту для всяких задач обмена данными — это делается так: источником сначала ложится файл флаг — затем загружаются файлы — когда файлы загружены флаг удаляется, клиент проверяет наличие файла флага — если есть то ничего не делает и ждёт дальше — если нет — скачивает.
Можно ещё в сам файл-флаг разную информацию помещать, для информирования клиента.
Большое спасибо за оперативный ответ.
>>источником сначала ложится файл флаг — затем загружаются файлы — когда файлы загружены флаг удаляется, клиент проверяет наличие файла флага — если есть то ничего не делает и ждёт дальше — если нет — скачивает.<<
— это знакомо, у меня другая ситуация, хотелось примерно так реализовать:
public bool ReadFTPFiles(string fileFtp);
{
filePatch = «ftp://localhost/» + fileFtp;
try
{
using (var fs = File.Open(filePatch, FileMode.Open, FileAccess.Read, FileShare.None))
{
return true;
}
}
catch (IOException ioex)
{
SkipFiles = +1;
return false;
}
}
Буду дальше думать, спасибо!
Всех благ…
Спасибо тебе огромное! Отличная статья! Очень долго искал, наконец, нашел! Все работает (переводил с C# на PascalABC.NET).
P.S. Я даже зарегистрировался, чтобы поблагодарить )
Спасибо за статью!
Подскажите, как переименовывать папки и файлы а фтп?
try
{
client.RenameFile(MainParams.FtpTimeout * 1000, "1", "2");
treeView1.Nodes.Clear();
GetItemsFromFtp();
}
Так работает, но как сделать чтобы была возможность вводить новое имя в окне программы? Ответить
Подскажи пожалуйста, можно ли как-нибудь отключить окна которые выскакивают при ошибке? А то приходится на удаленный сервка конектиться и нажимать кнопку окей?
ОтветитьА как задать чтобы файл с сервера загружался только если его дата создания новее чем файла на диске?
ОтветитьУ FtpItem — есть свойство Date — с его помощью можно получить время изменения файла.
В примере — если поменять функцию для вывода файлов вот так:
//Получение списка файлов текущего каталога с ФТП
private void GetItemsFromFtp()
{
foreach (FtpItem item in client.GetDirectoryList(MainParams.FtpTimeout * 1000))
{
TreeNode node = new TreeNode(item.Name);
node.ImageIndex = 2;
node.SelectedImageIndex = 2;
node.Tag = 0;
if (item.ItemType == FtpItemType.Directory)
{
node.ImageIndex = 0;
node.SelectedImageIndex = 0;
node.Tag = 1;
}
if (item.ItemType == FtpItemType.File)
{
node.ImageIndex = 1;
node.SelectedImageIndex = 1;
node.Text = node.Text +" --- " + item.Date.ToString();
}
if (item.ItemType == FtpItemType.Link)
{
node.ImageIndex = 3;
node.SelectedImageIndex = 3;
}
treeView1.Nodes.Add(node);
}
}
То будет выводит время изменения после имени файла. Ответить
Добрый день. При попытке получить файл с фтп выдаётся ошибка «Отказано в доступе по пути D:\....». Если не трудно, помогите найти ошибку.
ОтветитьНужен код — и на какой строчке вылетает ошибка.
Похоже что дело не в той части где идёт получение файла с ФТП а в той где идёт его приём на диск D:\…
Спасибо, что ответили. Код вечером предоставлю, но дело в том, что эта ошибка вылетает, даже в вашем коде. Более того, даже если запускаю тот бинарник, который уже скомпилирован вами. Думал, что может это из-за семёрки, но проверил сейчас на XP, результат тот же.
И ещё вопрос, как должна выглядеть строка скачивания файлов, что бы они сохранялись в процессе получения списка файлов?
client.GetFile(MainParams.FtpTimeout * 1000, «D:\путь:», item.Name); — такой вариант пойдёт?
код на connect и disconnect остался тот который был. Убран код на удаление, копирование и загрузку файлов. Изменилась только часть кода для получения списка файлов. Теперь программа получает список файлов из определенной директории, и только с указанной датой.
//Получение списка файлов текущего каталога с ФТП
private void GetItemsFromFtp()
{
foreach (FtpItem item in client.GetDirectoryList(MainParams.FtpTimeout * 1000))
{
if ((item.ItemType == FtpItemType.File) && (item.Date.Date == dateTimePicker1.Value.Date))
{
TreeNode node = new TreeNode(item.Name);
node.ImageIndex = 2;
node.SelectedImageIndex = 2;
node.Tag = 0;
node.ImageIndex = 1;
node.SelectedImageIndex = 1;
treeView1.Nodes.Add(node);
client.GetFile(MainParams.FtpTimeout * 1000, @«D:ftp», "/opt/ftp");
}
}
}
строчку сохранения не так написал. Это просто экспериментировал с разными вариантами. Сейчас у меня как и писал выше:
client.GetFile(MainParams.FtpTimeout * 1000, @«D:\ftp», item.Name);
Сразу не обратил внимания.
А так пробовал?
client.GetFile(MainParams.FtpTimeout * 1000, @«D:\\ftp», item.Name);
Ответить
Если папка ftp уже есть, то по прежнему «Отказано в доступе по пути...». Если папки ftp нету, то создаётся пустой файл ftp.
Дело вряд ли в неправильности пути, эта ошибка возникает даже при таком варианте:
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
client.GetFile(MainParams.FtpTimeout * 1000, folderBrowserDialog1.SelectedPath, item.Name);
}
У себя проверил, так работает:
try
{
if (treeView1.SelectedNode.Tag.ToString() == "0")
{
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
client.GetFile(MainParams.FtpTimeout * 1000, folderBrowserDialog1.SelectedPath + "\\" + treeView1.SelectedNode.Text, treeView1.SelectedNode.Text);
}
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
Там не просто путь надо — а имя файла, в какой конкретно файл надо скопировать. Ошибочка видимо вкралась.
Ну и после этого он у меня ругался на доступ — только когда к папкам реально доступа нет. А в остальных случая — копировалось.
На XP — похоже таких проблем не было. (Пример написан был на XP.) Ответить
Опишите подробней
//загружаем файл на сервер.
client.PutFile(TimeoutFTP, "имя файла на сервере", "что грузим - имя файла на компьютере");
Приведите пример как указывать путь и имя? Как задать маску? Что-то у меня вопще не получается загрузить файлы на сервер по маске. Ответить
Не знаю, ответите ли вы еще… я новичок и не в курсе, что и как происходит… хотелось бы узнать реально ли будет таким образом оперативно работать с БД на FTP… возможен ли вариант сразу нескольких подключений к этой БД и работе с ней (изменение БД)… я так понял, что работа будет заключаться в обновлении БД, т е ее перезаписи?
ОтветитьЧто значит БД? С базами данных работают по другому. Я метод скачивания по ФТП рассматриваю как вариант прогрузки необходимых компонентов для обновления. «Грубо говоря наше ПО ткнулось в какую нибудь службу на сервере по определенному порту, та ей ответила что взять где взять и выдала пароль. Программа полезла на ФТП и сама скачала всё что надо.» Как-то так…
ОтветитьВ этой программе реализовано скачивание файла с фтп сервера к себе на компьютер. А как сделать, чтобы скачивалась папка со всем его содержимым, включая вложенные директории с файлами и директориями внутри соответственно?
ОтветитьВыше. В комментариях, уже практически есть ответ на ваш вопрос.
Ответитьчто то не могу найти… помогите пожалуйста, очень заинтересовал этот вопрос
foreach (FtpItem item in client.GetDirectoryList(таймаут))//Перебираем итемы в папке
{
if (item.ItemType == FtpItemType.File)
{
//Если итем — является файлом (помимо файла могут быть ссылкой и директорией),
//И загружаем если необходимо по условию.
}
}
Попробовал с этим, не могу понять условие прохода по папке… и на путь при сохранении на комп ругается. Заранее благодарен, запутался совсем…
Если я правильно помню, то здесь подразумевается обыкновенная рекурсия.
Что касается ошибки при сохранении, которую я могу только угадывать, но рискну предположить что ругается на несуществующую директорию, её тоже надо создавать прежде чем положить в неё файл.
Работа с Postgresql в C#
Не так давно занялся разработкой приложения на C# работающего на базе PostreSQL, появилась информаци (читать далее...)
396Работа с MySQL в C#
Надеюсь данная публикация не даст читателям повторить мои ошибки и поможет сделать изначально правил (читать далее...)
316Распознавание автомобильного номера с изображения на C# (.NET)
Довелось мне не так давно помогать с запуском одного проекта под .NET на WCF — под x64 систему, прое (читать далее...)
248Изменение размера изображения в C#
Очень часто при добавлении изображений в проект приходится изменять размеры изображения, и часто хоч (читать далее...)
231OpenXML - Пример создания Excel файла на C#
В данной публикации приведен пример генерации Excel файла при помощи библиотеки OpenXML. Отличие это (читать далее...)
222
При попытке данной программой что то скачать с сервера ftp.microsoft.com на жёсткий диск возникает сообщение Access to the path 'xxxxxxx' is denied. Как разрешить доступ к папке?
ОтветитьУ меня всё работает вот с такими параметрами:


ОтветитьВот результат: