예제 #1
0
        /// <summary>
        /// The actual functionality to download with optional hash verification
        /// subclasses that wish to return the contents of the downloaded file
        /// or do something else with it can override this instead of RunWithReturn.
        /// </summary>
        /// <param name="success"></param>
        /// <returns></returns>
        protected virtual NPath RunDownload(bool success)
        {
            Exception exception   = null;
            var       attempts    = 0;
            bool      result      = false;
            var       partialFile = TargetDirectory.Combine(Filename + ".partial");

            TargetDirectory.EnsureDirectoryExists();
            do
            {
                exception = null;

                if (Token.IsCancellationRequested)
                {
                    break;
                }

                try
                {
                    Logger.Trace($"Download of {Url} to {Destination} Attempt {attempts + 1} of {RetryCount + 1}");

                    using (var destinationStream = fileSystem.OpenWrite(partialFile, FileMode.Append))
                    {
                        result = Downloader.Download(Logger, Url, destinationStream,
                                                     (value, total) =>
                        {
                            UpdateProgress(value, total);
                            return(!Token.IsCancellationRequested);
                        });
                    }

                    if (result)
                    {
                        partialFile.Move(Destination);
                    }
                }
                catch (Exception ex)
                {
                    exception = ex;
                    result    = false;
                }
            } while (!result && attempts++ < RetryCount);

            if (!result)
            {
                Token.ThrowIfCancellationRequested();
                throw new DownloadException("Error downloading file", exception);
            }

            return(Destination);
        }
예제 #2
0
        async Task DownloadAsync(HttpClient httpClient, CancellationToken cancellationToken)
        {
            var response = (await httpClient.GetAsync(
                                SourceUri,
                                HttpCompletionOption.ResponseHeadersRead,
                                cancellationToken)).EnsureSuccessStatusCode();

            cancellationToken.ThrowIfCancellationRequested();

            ActualSourceUri = response.Content.Headers.ContentLocation ?? SourceUri;
            TotalBytes      = (ulong)response.Content.Headers.ContentLength.GetValueOrDefault();

            var _targetFile = TargetDirectory.Combine(Path.GetFileName(ActualSourceUri.LocalPath));

            using (var hash = MD5.Create())
                using (var sourceStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
                    using (var outputStream = CreateOutputStream(ref _targetFile)) {
                        cancellationToken.ThrowIfCancellationRequested();

                        TargetFile = _targetFile;

                        var buffer = new byte [bufferSize];
                        int read;

                        while ((read = await sourceStream
                                       .ReadAsync(buffer, 0, buffer.Length, cancellationToken)
                                       .ConfigureAwait(false)) != 0)
                        {
                            cancellationToken.ThrowIfCancellationRequested();

                            await outputStream
                            .WriteAsync(buffer, 0, read, cancellationToken)
                            .ConfigureAwait(false);

                            hash.TransformBlock(buffer, 0, read, buffer, 0);

                            ProgressBytes += (ulong)read;
                            Progress       = TotalBytes == 0 ? 0.0 : ProgressBytes / (double)TotalBytes;
                        }

                        hash.TransformFinalBlock(buffer, 0, 0);

                        await outputStream
                        .FlushAsync(cancellationToken)
                        .ConfigureAwait(false);

                        var fileLength = new FileInfo(TargetFile).Length;
                        if (fileLength != (long)TotalBytes)
                        {
                            throw new DamagedDownloadException(
                                      $"expected {TotalBytes} bytes for {TargetFile} " +
                                      $"but size on disk is {fileLength} bytes");
                        }

                        if (Md5Hash != null)
                        {
                            var actualHash = hash.Hash.ToHexString();
                            if (!String.Equals(Md5Hash, actualHash, StringComparison.OrdinalIgnoreCase))
                            {
                                throw new DamagedDownloadException(
                                          $"checksum ({actualHash}) for {TargetFile} does " +
                                          $"not match expected checksum {Md5Hash}");
                            }
                        }
                    }
        }