我有以下方法从FTP服务器下载文件:
public bool DownloadFTP(string LocalDirectory, string RemoteFile,
string Login, string Password)
{
try
{
FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(RemoteFile);
ftp.Credentials = new NetworkCredential(Login, Password);
ftp.KeepAlive = false;
ftp.Method = WebRequestMethods.Ftp.DownloadFile;
Form1 form = new Form1();
ftp.UseBinary = true;
long pesoarchivo = obtenertamano(RemoteFile, Login, Password);
ftp.Proxy = null;
ftp.EnableSsl = false;
LocalDirectory = Path.Combine(LocalDirectory, Path.GetFileName(RemoteFile));
using (FileStream fs = new FileStream(LocalDirectory, FileMode.Create,
FileAccess.Write, FileShare.None))
using (Stream strm = ftp.GetResponse().GetResponseStream())
{
int buffLength = 2048;
byte[] buff = new byte[buffLength];
// Leer del buffer 2kb cada vez
int contentLen=strm.Read(buff, 0, buffLength);
int bytes = 0;
try
{
while (contentLen > 0)
{
fs.Write(buff, 0, contentLen);
contentLen = strm.Read(buff, 0, buffLength);
bytes += contentLen;
int totalSize = (int)(pesoarchivo) / 1000;
if (bytes != 0)
{
// Console.WriteLine(((bytes / 1000) * 100 / totalSize).ToString()+" "+ totalSize.ToString());
backgroundWorker1.ReportProgress((bytes / 1000) * 100 / totalSize, totalSize);
// label1.Text = ((bytes / 1000) * 100 / totalSize).ToString();
}
else { }
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString()) }
}
return true;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString())
return false;
}
然后我有一个使用按钮调用的BackgroundWorker。 在BackgroundWorker" DoWork"事件我调用DownloadFTP方法:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (descargaFTP(folder + "\\folder\\", ftpServer+ file, User, Password))
{
MessageBox.Show("Download Completed");
}
}
我想添加一个按钮来取消FTPDownload。我该怎么办?
答案 0 :(得分:1)
要做到这一点,首先要删除BackgroundWorker
并使用FtpWebRequest
类的异步成员。据我所知,同步方法不可取消,但您可以使用FtpWebRequest.Abort()
方法取消异步操作。
例如:
// This needs to be an instance field instead of local variable, so that
// the object's `Abort()` method can be called elsewhere
private FtpWebRequest ftp;
public async Task DownloadFTPAsync(CancellationToken cancelToken,
string LocalDirectory, string RemoteFile, string Login, string Password)
{
try
{
ftp = (FtpWebRequest)FtpWebRequest.Create(RemoteFile);
ftp.Credentials = new NetworkCredential(Login, Password);
ftp.KeepAlive = false;
ftp.Method = WebRequestMethods.Ftp.DownloadFile;
ftp.UseBinary = true;
// [PD]: I don't know what this method does or even what the name
// means, but on the assumption that because it needs the login
// and password information, it may be doing some longer network
// I/O or something, I'm wrapping it in a task to ensure the calling
// thread remains responsive.
long pesoarchivo = await Task.Run(() => obtenertamano(RemoteFile, Login, Password));
// This is invariant, so might as well calculate it here, rather
// than over and over again in the stream I/O loop below
int totalSize = (int)(pesoarchivo) / 1000;
ftp.Proxy = null;
ftp.EnableSsl = false;
LocalDirectory = Path.Combine(LocalDirectory, Path.GetFileName(RemoteFile));
using (FileStream fs = new FileStream(LocalDirectory, FileMode.Create,
FileAccess.Write, FileShare.None))
using (Stream strm = (await ftp.GetResponseAsync()).GetResponseStream())
{
int buffLength = 2048;
byte[] buff = new byte[buffLength];
int contentLen;
// Check for cancellation. This will throw an exception
// if cancellation was requested; if you'd prefer, you can
// check the cancelToken.IsCancellationRequested property
// instead and just return, or whatever.
cancelToken.ThrowIfCancellationRequested();
while ((contentLen = await strm.ReadAsync(buff, 0, buff.Length)) > 0)
{
await fs.WriteAsync(buff, 0, contentLen);
// Here, the calling thread (i.e. the UI thread) is executing
// the code so just update whatever progress indication
// you want directly.
label1.Text = ((fs.Position / 1000) * 100 / totalSize).ToString();
// Keep checking inside the loop
cancelToken.ThrowIfCancellationRequested();
}
}
}
finally
{
ftp = null;
}
}
注意:
Stream.CopyToAsync()
完全替换I / O循环。但是,当然在这种情况下,您将无法进行进度更新。我假设进度更新是一个重要的功能,所以我保留了上面的代码,即可以制作它们。async
方法可以将异常处理移动到调用者中,所以我将其删除并在那里进行整合。当然,如果你愿意,你可以这样做。使用上面实现的下载代码,您可以像这样调用它(例如):
CancellationTokenSource _cancelTokenSource;
async void downloadButton_Click(object sender, EventArgs e)
{
// User clicked the button to start download
_cancelTokenSource = new CancellationTokenSource();
try
{
// For example, update the UI button state so user can't start a
// new download operation until this one is done, but they can cancel
// this one.
downloadButton.Enabled = false;
cancelButton.Enabled = true;
// I assume you know where to get the other method arguments, i.e.
// from "LocalDirectory" on; your incomplete code example didn't
// provide that detail. :(
await DownloadFTPAsync(_cancelTokenSource.Token,
LocalDirectory, RemoteFile, Login, Password);
}
catch (OperationCanceledException)
{
MessageBox.Show("Download was cancelled by user.");
}
catch (Exception ex)
{
// Some other error occurred
MessageBox.Show(ex.ToString())
}
finally
{
downloadButton.Enabled = true;
cancelButton.Enabled = false;
_cancelTokenSource = null;
}
}
如果用户点击您的取消按钮,您可以像这样处理Click
事件:
void cancelButton_Click(object sender, EventArgs e)
{
ftp.Abort();
_cancelTokenSource.Cancel();
}
调用FtpWebRequest.Abort()
方法将中断任何实际的异步FTP I / O操作,当然调用CancellationTokenSource.Cancel()
将导致CancellationToken
表示请求取消。