C # Unduh semua file dan subdirektori melalui FTP
Info Umum
Saya masih dalam proses belajar C #. Untuk membantu diri saya sendiri, saya mencoba membuat program yang secara otomatis akan menyinkronkan semua proyek lokal saya dengan folder di server FTP saya. Ini agar baik saya di sekolah atau di rumah, saya selalu memiliki proyek yang sama tersedia untuk saya.
Saya tahu ada program seperti Dropbox yang sudah melakukan ini untuk saya, tetapi menurut saya membuat sesuatu seperti itu sendiri akan mengajari saya banyak hal selama prosesnya.
Masalahnya
Langkah pertama saya menuju tujuan saya adalah mengunduh semua file, subdirektori, dan subfile dari server FTP saya. Saya sudah berhasil mendownload semua file dari direktori dengan kode di bawah ini. Namun, kode saya hanya mencantumkan nama folder dan file di direktori utama. Subfolder dan subfile tidak pernah dikembalikan dan tidak pernah diunduh. Selain itu, server mengembalikan kesalahan 550 karena saya mencoba mengunduh folder seolah-olah itu adalah file. Saya sudah melakukan ini selama 4+ jam sekarang, tetapi saya tidak dapat menemukan apa pun tentang cara memperbaiki masalah ini dan membuatnya berfungsi. Oleh karena itu saya berharap kalian akan membantu saya :)
Kode
public string[] GetFileList()
{
string[] downloadFiles;
StringBuilder result = new StringBuilder();
WebResponse response = null;
StreamReader reader = null;
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
request.UseBinary = true;
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord);
request.KeepAlive = false;
request.UsePassive = false;
response = request.GetResponse();
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);
return result.ToString().Split('\n');
}
catch (Exception ex)
{
if (reader != null)
{
reader.Close();
}
if (response != null)
{
response.Close();
}
downloadFiles = null;
return downloadFiles;
}
}
private void Download(string file)
{
try
{
string uri = url + "/" + file;
Uri serverUri = new Uri(uri);
if (serverUri.Scheme != Uri.UriSchemeFtp)
{
return;
}
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url + "/" + file);
request.UseBinary = true;
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord);
request.KeepAlive = false;
request.UsePassive = false;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
FileStream writeStream = new FileStream(localDestnDir + "\\" + file, FileMode.Create);
int Length = 2048;
Byte[] buffer = new Byte[Length];
int bytesRead = responseStream.Read(buffer, 0, Length);
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = responseStream.Read(buffer, 0, Length);
}
writeStream.Close();
response.Close();
}
catch (WebException wEx)
{
MessageBox.Show(wEx.Message, "Download Error");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Download Error");
}
}
Jawaban
Tidak FtpWebRequestmemiliki dukungan eksplisit apa pun untuk operasi file rekursif (termasuk unduhan). Anda harus menerapkan rekursi sendiri:
- Buat daftar direktori jarak jauh
- Iterasi entri, unduh file, dan ulangi ke subdirektori (buat daftar lagi, dll.)
Bagian yang rumit adalah mengidentifikasi file dari subdirektori. Tidak ada cara untuk melakukan itu dengan cara portabel dengan file FtpWebRequest. The FtpWebRequestsayangnya tidak mendukung MLSDperintah, yang merupakan satu-satunya cara portabel untuk mengambil daftar direktori dengan atribut file dalam protokol FTP. Lihat juga Memeriksa apakah objek di server FTP adalah file atau direktori .
Pilihan Anda adalah:
- Lakukan operasi pada nama file yang pasti gagal untuk file dan berhasil untuk direktori (atau sebaliknya). Yaitu Anda dapat mencoba mengunduh "nama". Jika berhasil, itu adalah file, jika gagal, itu adalah direktori.
- Anda mungkin beruntung dan dalam kasus khusus Anda, Anda dapat mengetahui file dari direktori dengan nama file (yaitu semua file Anda memiliki ekstensi, sementara subdirektori tidak)
- Anda menggunakan daftar direktori yang panjang (
LISTcommand =ListDirectoryDetailsmethod) dan mencoba mengurai daftar khusus server. Banyak server FTP menggunakan daftar gaya * nix, di mana Anda mengidentifikasi direktoriddi awal entri. Tetapi banyak server menggunakan format yang berbeda. Contoh berikut menggunakan pendekatan ini (dengan asumsi format * nix)
void DownloadFtpDirectory(string url, NetworkCredential credentials, string localPath)
{
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 localFilePath = Path.Combine(localPath, name);
string fileUrl = url + name;
if (permissions[0] == 'd')
{
if (!Directory.Exists(localFilePath))
{
Directory.CreateDirectory(localFilePath);
}
DownloadFtpDirectory(fileUrl + "/", credentials, localFilePath);
}
else
{
FtpWebRequest downloadRequest = (FtpWebRequest)WebRequest.Create(fileUrl);
downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile;
downloadRequest.Credentials = credentials;
using (FtpWebResponse downloadResponse =
(FtpWebResponse)downloadRequest.GetResponse())
using (Stream sourceStream = downloadResponse.GetResponseStream())
using (Stream targetStream = File.Create(localFilePath))
{
byte[] buffer = new byte[10240];
int read;
while ((read = sourceStream.Read(buffer, 0, buffer.Length)) > 0)
{
targetStream.Write(buffer, 0, read);
}
}
}
}
}
Gunakan fungsi seperti:
NetworkCredential credentials = new NetworkCredential("user", "mypassword");
string url = "ftp://ftp.example.com/directory/to/download/";
DownloadFtpDirectory(url, credentials, @"C:\target\directory");
Jika Anda ingin menghindari masalah dengan penguraian format daftar direktori khusus server, gunakan pustaka pihak ketiga yang mendukung MLSDperintah dan / atau parsing berbagai LISTformat daftar; dan unduhan rekursif.
Misalnya dengan perakitan WinSCP .NET Anda dapat mengunduh seluruh direktori dengan satu panggilan ke Session.GetFiles:
// 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);
// Download files
session.GetFiles("/directory/to/download/*", @"C:\target\directory\*").Check();
}
Secara internal, WinSCP menggunakan MLSDperintah, jika didukung oleh server. Jika tidak, ia menggunakan LISTperintah dan mendukung lusinan format daftar yang berbeda.
The Session.GetFilesMetode rekursif secara default.
(Saya adalah penulis WinSCP)