//// Here we receive the response from the server. We do not check for the "Accept-Ranges" //// response header, in order to find out if the server supports resuming, because it MAY //// send the "Accept-Ranges" response header, but is not required to do so. This is //// unreliable, so we'll just continue and catch the exception that will occur if not //// supported and send it the DownloadCompleted event. We also don't check if the //// Content-Length is '-1', because some servers return '-1', eventhough the file/webpage //// you're trying to download is valid. e.ProgressPercentage returns '-1' in that case. private void GetResponse_Callback(IAsyncResult result) { HttpWebRequestState State = (HttpWebRequestState)result.AsyncState; FileStream DestinationStream = null; HttpWebResponse Response = null; Stopwatch Duration = new Stopwatch(); byte[] Buffer = new byte[8192]; int BytesRead = 0; int ElapsedSeconds = 0; int DownloadSpeed = 0; int DownloadProgress = 0; int BytesReceivedThisSession = 0; try { //'// Get response Response = (HttpWebResponse)(State.Request.EndGetResponse(result)); //// Asign Response headers to ReadOnly ResponseHeaders property. _ResponseHeaders = Response.Headers; //// If the server does not reply with an 'OK (200)' message when starting //// the download or a 'PartialContent (206)' message when resuming. if ((int)Response.StatusCode != System.Convert.ToInt32(HttpStatusCode.OK) & (int)Response.StatusCode != System.Convert.ToInt32(HttpStatusCode.PartialContent)) { //// Send error message to anyone who is listening. OnDownloadCompleted(new FileDownloadCompletedEventArgs(new Exception(Response.StatusCode.ToString()), false, State.userToken)); return; } //// Create/open the file to write to. if (State.ResumeDownload) { //// If resumed, then create or open the file. DestinationStream = new FileStream(State.LocalFilePath, FileMode.OpenOrCreate, FileAccess.Write); } else { //// If not resumed, then create the file, which will delete the existing file if it already exists. DestinationStream = new FileStream(State.LocalFilePath, FileMode.Create, FileAccess.Write); //// Get the ContentLength only when we're starting the download. Not when resuming. _ContentLenght = Response.ContentLength; } //// Moves stream position to beginning of the file when starting the download. //// Moves stream position to end of the file when resuming the download. DestinationStream.Seek(0, SeekOrigin.End); //// Start timer to get download duration / download speed, etc. Duration.Start(); //// Get the Response Stream. using (Stream responseStream = Response.GetResponseStream()) { do { //// Read some bytes. BytesRead = responseStream.Read(Buffer, 0, Buffer.Length); if (BytesRead > 0) { //// Write incoming data to the file. DestinationStream.Write(Buffer, 0, BytesRead); //// Count the total number of bytes downloaded. _TotalBytesReceived += BytesRead; //// Count the number of bytes downloaded this session (Resume). BytesReceivedThisSession += BytesRead; //// Get number of elapsed seconds (need round number to prevent 'division by zero' error). ElapsedSeconds = System.Convert.ToInt32(Duration.Elapsed.TotalSeconds); //// Update frequency: No Delay, every Half a Second or every Second. if (ProgressUpdateFrequency == UpdateFrequency.NoDelay) { //// Calculate download speed in bytes per second. if (ElapsedSeconds > 0) { DownloadSpeed = BytesReceivedThisSession / ElapsedSeconds; } //// Send download progress to anyone who is listening. OnDownloadProgressChanged(new FileDownloadProgressChangedEventArgs(_TotalBytesReceived, (int)_ContentLenght, ElapsedSeconds, DownloadSpeed, State.userToken)); } else if (ProgressUpdateFrequency == UpdateFrequency.HalfSecond) { if (System.Convert.ToInt32(Duration.ElapsedMilliseconds - DownloadProgress) >= 500) { DownloadProgress = (int)Duration.ElapsedMilliseconds; //// Calculate download speed in bytes per second. if (ElapsedSeconds > 0) { DownloadSpeed = BytesReceivedThisSession / ElapsedSeconds; } //// Send download progress to anyone who is listening. OnDownloadProgressChanged(new FileDownloadProgressChangedEventArgs(_TotalBytesReceived, (int)_ContentLenght, ElapsedSeconds, DownloadSpeed, State.userToken)); } } else if (ProgressUpdateFrequency == UpdateFrequency.Second) { if (System.Convert.ToInt32(Duration.ElapsedMilliseconds - DownloadProgress) >= 1000) { DownloadProgress = (int)Duration.ElapsedMilliseconds; //// Calculate download speed in bytes per second. if (ElapsedSeconds > 0) { DownloadSpeed = BytesReceivedThisSession / ElapsedSeconds; } //// Send download progress to anyone who is listening. OnDownloadProgressChanged(new FileDownloadProgressChangedEventArgs(_TotalBytesReceived, (int)_ContentLenght, ElapsedSeconds, DownloadSpeed, State.userToken)); } } //// Exit loop when paused. if (_CancelAsync) { break; } } } while (!(BytesRead == 0)); } //// Send download progress once more. If the UpdateFrequency has been set to //// HalfSecond or Second, then the last percentage returned might be 98% or 99%. //// This makes sure it's 100%. OnDownloadProgressChanged(new FileDownloadProgressChangedEventArgs(_TotalBytesReceived, (int)_ContentLenght, System.Convert.ToInt32(Duration.Elapsed.TotalSeconds), DownloadSpeed, State.userToken)); if (_CancelAsync) { //// Send completed message (Paused) to anyone who is listening. OnDownloadCompleted(new FileDownloadCompletedEventArgs(null, true, State.userToken)); } else { //// Send completed message (Finished) to anyone who is listening. OnDownloadCompleted(new FileDownloadCompletedEventArgs(null, false, State.userToken)); } } catch (Exception ex) { //// Send completed message (Error) to anyone who is listening. OnDownloadCompleted(new FileDownloadCompletedEventArgs(ex, false, State.userToken)); } finally { //// Close the file. if (DestinationStream != null) { DestinationStream.Flush(); DestinationStream.Close(); DestinationStream = null; } //// Stop and reset the duration timer. Duration.Reset(); Duration = null; //// Signal we're not downloading anymore. _isbusy = false; } }
protected override void BeforeBlockWrite([NotNull] Block block, [NotNull] PipelineInfo pipelineInfo) { var offset = (long)block.Position * pipelineInfo.BlockSize; DestinationStream.Seek(offset, SeekOrigin.Begin); }