简体   繁体   English

使用 HttpWebRequest C# 下载文件部分

[英]Downloading file parts using HttpWebRequest C#

I am trying to download a 100GB file using HttpWebRequest.我正在尝试使用 HttpWebRequest 下载一个 100GB 的文件。 The download will be split into parts depending on a preset part size.下载将根据预设的部分大小分成几部分。 Below is the code I use to download the file:下面是我用来下载文件的代码:

private static void AddRangeHeaderHack(WebHeaderCollection headers, long start, long end)
        {
            // Original workaround by Eric Cadwell, code taken from
            // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=93714
            Type type = headers.GetType();

            System.Reflection.MethodInfo setAddVerified = type.GetMethod("SetAddVerified",
                System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy
                );

            string rangeHeaderValue = String.Format("bytes={0}-{1}", start, end);
            if (setAddVerified != null)
                setAddVerified.Invoke(headers, new object[] { "Range", rangeHeaderValue });
        }

        private ulong GetRemoteFileSize(string URI)
        {
            ulong size = 0;
            HttpWebRequest req = null;
            try
            {
                req = (HttpWebRequest)WebRequest.Create(URI);
                using (var res = (HttpWebResponse)req.GetResponse())
                {
                    size = (ulong)res.ContentLength;
                    res.Close();
                }
            }
            catch (Exception ex)
            {

            }

            if (req != null)
            {
                try
                {
                    req.Abort();
                    req = null;
                }
                catch (Exception)
                {

                }
            }

            return size;
        }

        private int DownloadFromLink(string sSource, string sDestination)
        {
            int nRetryCount = 0;
            int nMaxRetry = 5;
            var lastProgress = DateTime.Now;
            ulong offset = 0;
            var bRetrying = false;
            var bResumable = false;

            var fileSize = GetRemoteFileSize(sSource);
            if (fileSize > 0)
                bResumable = true;

            while (true)
            {
                HttpWebRequest webRequest = null;
                try
                {
                    try
                    {
                        bRetrying = false;
                        do
                        {
                            try
                            {
                                if (bDownloadAbort)
                                {
                                    return -1;
                                }

                                webRequest = (HttpWebRequest)WebRequest.Create(sSource);
                                webRequest.Timeout = 3600000;

                                if (offset > 0)
                                {
                                    AddRangeHeaderHack(webRequest.Headers, (long)offset, (long)fileSize);
                                }

                                // Retrieve the response from the server
                                using (var webResponse = (HttpWebResponse)webRequest.GetResponse())
                                {

                                    var acceptRanges = String.Compare(webResponse.Headers["Accept-Ranges"], "bytes", true) == 0;

                                    // Open the URL for download 
                                    using (var streamResponse = webResponse.GetResponseStream())
                                    {
                                        if (streamResponse != null)
                                        {
                                            // Create a new file stream where we will be saving the data (local drive)
                                            using (var streamLocal = new FileStream(sDestination, offset>0?FileMode.Append:FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
                                            {
                                                // It will store the current number of bytes we retrieved from the server
                                                int bytesSize = 0;
                                                // A buffer for storing and writing the data retrieved from the server
                                                byte[] downBuffer = new byte[/*16384*/ 1024 * 1024];
                                                bool binitialtry = true;
                                                int nRetries = 0;

                                                if (offset > 0)
                                                {
                                                    streamLocal.Seek((long)offset, SeekOrigin.Begin);
                                                }

                                                // Loop through the buffer until the buffer is empty
                                                while ((bytesSize = streamResponse.Read(downBuffer, 0, downBuffer.Length)) > 0 
                                                    || (File.Exists(sDestination) && (offset < (ulong)fileSize) && nRetries < 5 && bResumable))
                                                {
                                                    if (binitialtry && bytesSize == 0)
                                                    {
                                                        binitialtry = false;
                                                    }

                                                    if (!binitialtry && bytesSize == 0)
                                                    {
                                                        nRetries++;
                                                        bRetrying = nRetries<5;
                                                        break;
                                                    }

                                                    if (bDownloadAbort)
                                                    {
                                                        try { streamLocal.Close(); }
                                                        catch { }

                                                        return;
                                                    }

                                                    try
                                                    {
                                                        // Write the data from the buffer to the local hard drive
                                                        streamLocal.Write(downBuffer, 0, bytesSize);
                                                        offset += (ulong)bytesSize;
                                                    }
                                                    catch (IOException ex)
                                                    {
                                                        if (streamResponse != null)
                                                            streamResponse.Close();

                                                        if (streamLocal != null)
                                                            streamLocal.Close();

                                                        if (webRequest != null)
                                                            webRequest.Abort();

                                                        return -1;
                                                    }

                                                    Interlocked.Add(ref actualDownloaded, bytesSize);
                                                }

                                                // When the above code has ended, close the streams
                                                if (streamResponse != null)
                                                    streamResponse.Close();

                                                if (streamLocal != null)
                                                    try { streamLocal.Close(); }
                                                    catch { }

                                                if (webRequest != null)
                                                    webRequest.Abort();

                                                if (webRequest != null)
                                                    wcDownload.Dispose();

                                                streamLocal.Close();
                                            }
                                            streamResponse.Close();
                                        }
                                    }
                                    webResponse.Close();
                                }

                                if(!bRetrying)
                                    break;
                            }
                            catch (IOException ex)
                            {
                                if (webRequest != null)
                                    webRequest.Abort();

                                if (wcDownload != null)
                                    wcDownload.Dispose();

                                if (nRetryCount <= nMaxRetry)
                                {
                                    Thread.Sleep(10000);
                                    nRetryCount++;
                                    bRetrying = true;
                                }
                                else
                                {
                                    break;
                                }
                            }
                            catch (UnauthorizedAccessException ex)
                            {
                                if (webRequest != null)
                                    webRequest.Abort();

                                break;
                            }
                            catch (WebException ex)
                            {
                                if (webRequest != null)
                                    webRequest.Abort();

                                if (wcDownload != null)
                                    wcDownload.Dispose();

                                if (nRetryCount <= nMaxRetry)
                                {
                                    Thread.Sleep(10000);
                                    nRetryCount++;
                                    bRetrying = true;
                                }
                                else
                                {
                                    break;
                                }
                            }
                            finally
                            {


                            }

                        } while (bRetrying);


                    }
                    catch (Exception ex)
                    {
                        break;
                    }
                }
                catch
                {
                    break;
                }

                if(!bRetrying)
                    break;
            }
        }

If I try to download the file in 1 part, with out adding the range header, the code runs smoothly and the file downloads normally.如果我尝试分 1 部分下载文件,不添加范围标头,代码运行顺利,文件下载正常。 When I add a range header, say from 10GB to 15GB or frankly any value, the code reaches streamResponse.Read and hangs there for several minutes then it throws an exception:当我添加一个范围标头时,比如从 10GB 到 15GB 或坦率地说任何值,代码到达streamResponse.Read并在那里挂起几分钟,然后它抛出异常:

Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host无法从传输连接读取数据:现有连接被远程主机强行关闭

When the code retries the connection after the exception, the download resumes normally and the client is able to read data from the stream.当代码在异常后重试连接时,下载恢复正常并且客户端能够从流中读取数据。

Can someone help me determine why such thing is happening?有人可以帮我确定为什么会这样吗?

Just to clear the matter about the server, the file is currently hosted on an Amazon S3 server, and the download is done from a generated direct link.只是为了澄清有关服务器的问题,该文件目前托管在 Amazon S3 服务器上,下载是从生成的直接链接完成的。

It could be a server setting, according to http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.4 根据http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.4 ,它可能是服务器设置

Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. 使用持久连接的客户端应该限制它们与给定服务器同时维护的连接数量。 A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy. 单用户客户端不应与任何服务器或代理保持2个以上的连接。 A proxy SHOULD use up to 2*N connections to another server or proxy, where N is the number of simultaneously active users. 代理最多应使用2 * N个连接到另一个服务器或代理的连接,其中N是同时活动用户的数量。 These guidelines are intended to improve HTTP response times and avoid congestion. 这些准则旨在缩短HTTP响应时间并避免拥塞。

Try FDM, and see if it has a problem. 尝试FDM,看看是否有问题。 http://www.freedownloadmanager.org/ http://www.freedownloadmanager.org/

I don't know how to download a file in parts using HttpWebRequest, but I found this example online to build an own implementation.我不知道如何使用 HttpWebRequest 部分下载文件,但我在网上找到了这个示例来构建自己的实现。 The article is about the HttpClient in C#.这篇文章是关于 C# 中的 HttpClient 的。 There is also complete code and project you can find in the download section of this page.您还可以在本页的下载部分找到完整的代码和项目。

The Problem is that not all server support partial download.问题是并非所有服务器都支持部分下载。 So NuGet packages can be used that handle the exceptions eg: https://www.nuget.org/packages/downloader or https://www.nuget.org/packages/Shard.DonwloadLibrary .因此,可以使用 NuGet 包来处理异常,例如: https ://www.nuget.org/packages/downloader 或https://www.nuget.org/packages/Shard.DonwloadLibrary These Libraries will handle the chunks and convert them back into a readable file or stream.这些库将处理块并将它们转换回可读文件或流。

Downloader:下载器:

var downloader = new DownloadService(new()
    {
        ChunkCount = 8,
    });
    string file = @"Your_Path\fileName.zip";
    string url = @"https://file-examples.com/fileName.zip";
    await downloader.DownloadFileTaskAsync(url, file);

or Download Library:或下载库:

    string file = "Your_Path";
    string url = "https://file-examples.com/fileName.zip";
    var downloader = new LoadRequest(url,new()
    {
        Chunks = 8,
        DestinationPath= file,
    });

    await downloader.Task;

I hope I could help!我希望我能帮上忙!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM