问题
I am creating a downloader in C#. I am using WebClient class. To pause downloading on a button click I could just think of using Thread. So when I created thread and attached it with my file downloading as below
WebClient web = new WebLCient();
Thread dwnd_thread = new Thread(Program.web.DownloadFileAsync(new Uri(Program.src), Program.dest));
it is giving me following errors: "The best overloaded method match for 'System.Threading.Thread.Thread(System.Threading.ThreadStart)' has some invalid arguments" and "Argument '1': cannot convert from 'void' to 'System.Threading.ThreadStart'".
Then I thought if I pause my system main thread then it could block my whole process for that I used below line of code
System.Threading.Thread.Sleep(100);
but it is doing nothing at all. Can somebody tell me what could be a better approach for pause/downloading and how to use thread to pause my downloading process.
回答1:
As there's no standard way of pause/resume a download request, you'll have to implement your own mechanism. Below is a block of code, containing an example of how such a mechanism could look. The class FileDownload
takes 3 parameters:
source
- url to the file, to download.destination
- where to save the file.chunkSize
- how many bytes to read, before checking whether to pause or continue the download.
public class FileDownload
{
private volatile bool _allowedToRun;
private string _source;
private string _destination;
private int _chunkSize;
private Lazy<int> _contentLength;
public int BytesWritten { get; private set; }
public int ContentLength { get { return _contentLength.Value; } }
public bool Done { get { return ContentLength == BytesWritten; } }
public FileDownload(string source, string destination, int chunkSize)
{
_allowedToRun = true;
_source = source;
_destination = destination;
_chunkSize = chunkSize;
_contentLength = new Lazy<int>(() => Convert.ToInt32(GetContentLength()));
BytesWritten = 0;
}
private long GetContentLength()
{
var request = (HttpWebRequest)WebRequest.Create(_source);
request.Method = "HEAD";
using (var response = request.GetResponse())
return response.ContentLength;
}
private async Task Start(int range)
{
if (!_allowedToRun)
throw new InvalidOperationException();
var request = (HttpWebRequest)WebRequest.Create(_source);
request.Method = "GET";
request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
request.AddRange(range);
using (var response = await request.GetResponseAsync())
{
using (var responseStream = response.GetResponseStream())
{
using (var fs = new FileStream(_destination, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
{
while (_allowedToRun)
{
var buffer = new byte[_chunkSize];
var bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0) break;
await fs.WriteAsync(buffer, 0, bytesRead);
BytesWritten += bytesRead;
}
await fs.FlushAsync();
}
}
}
}
public Task Start()
{
_allowedToRun = true;
return Start(BytesWritten);
}
public void Pause()
{
_allowedToRun = false;
}
}
Usage:
static void Main(string[] args)
{
var fw = new FileDownload("http://download.microsoft.com/download/E/E/2/EE2D29A1-2D5C-463C-B7F1-40E4170F5E2C/KinectSDK-v1.0-Setup.exe", @"D:\KinetSDK.exe", 5120);
// Display progress...
Task.Factory.StartNew(() =>
{
while (!fw.Done)
{
Console.SetCursorPosition(0, Console.CursorTop);
Console.Write(string.Format("ContentLength: {0} | BytesWritten: {1}", fw.ContentLength, fw.BytesWritten));
}
});
// Start the download...
fw.Start();
// Simulate pause...
Thread.Sleep(500);
fw.Pause();
Thread.Sleep(2000);
// Start the download from where we left, and when done print to console.
fw.Start().ContinueWith(t => Console.WriteLine("Done"));
Console.ReadKey();
}
回答2:
unfortunately WebClient has no ways to pause downloading. So, You must use WebRequest on background thread and pause getting response stream with flag. Here is sample code. But make sure that you can't pause unlimited, because TCP connection will be closed if there has nothing transfer for a while. So if resume downloading fail, you must restart downloading again.
public class DownloadJob {
public delegate void DownloadCompletedDelegate(Stream stream);
//completion download event
public event DownloadCompletedDelegate OnDownloadCompleted;
//sync object
private object _lock = new object();
//pause flag
private bool _bPause = false;
//Pause download
public void Pause() {
lock (_lock) {
_bPause = true;
}
}
//Resume download
public void Resume() {
lock (_lock) {
_bPause = false;
}
}
//Begin download with URI
public void BeginDowload(Uri uri) {
//Create Background thread
Thread downLoadThread = new Thread(
delegate() {
WebRequest pWebReq = WebRequest.Create(uri);
WebResponse pWebRes = pWebReq.GetResponse();
using (MemoryStream pResultStream = new MemoryStream())
using (Stream pWebStream = pWebRes.GetResponseStream()) {
byte[] buffer = new byte[256];
int readCount = 1;
while (readCount > 0) {
//Read download stream
readCount = pWebStream.Read(buffer, 0, buffer.Length);
//Write to result MemoryStream
pResultStream.Write(buffer, 0, readCount);
//Waiting 100msec while _bPause is true
while (true) {
lock (_lock) {
if (_bPause == true) {
Thread.Sleep(100);
}
else {
break;
}
}
}
pResultStream.Flush();
}
//Fire Completion event
if (OnDownloadCompleted != null) {
OnDownloadCompleted(pResultStream);
}
}
}
);
//Start background thread job
downLoadThread.Start();
}
}
来源:https://stackoverflow.com/questions/15995705/adding-pause-and-continue-ability-in-my-downloader