![](/img/trans.png)
[英]C# Downloading website into string using C# WebClient or HttpWebRequest
[英]Downloading file parts using HttpWebRequest C#
我正在嘗試使用 HttpWebRequest 下載一個 100GB 的文件。 下載將根據預設的部分大小分成幾部分。 下面是我用來下載文件的代碼:
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;
}
}
如果我嘗試分 1 部分下載文件,不添加范圍標頭,代碼運行順利,文件下載正常。 當我添加一個范圍標頭時,比如從 10GB 到 15GB 或坦率地說任何值,代碼到達streamResponse.Read
並在那里掛起幾分鍾,然后它拋出異常:
無法從傳輸連接讀取數據:現有連接被遠程主機強行關閉
當代碼在異常后重試連接時,下載恢復正常並且客戶端能夠從流中讀取數據。
有人可以幫我確定為什么會這樣嗎?
只是為了澄清有關服務器的問題,該文件目前托管在 Amazon S3 服務器上,下載是從生成的直接鏈接完成的。
根據http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.4 ,它可能是服務器設置
使用持久連接的客戶端應該限制它們與給定服務器同時維護的連接數量。 單用戶客戶端不應與任何服務器或代理保持2個以上的連接。 代理最多應使用2 * N個連接到另一個服務器或代理的連接,其中N是同時活動用戶的數量。 這些准則旨在縮短HTTP響應時間並避免擁塞。
嘗試FDM,看看是否有問題。 http://www.freedownloadmanager.org/
我不知道如何使用 HttpWebRequest 部分下載文件,但我在網上找到了這個示例來構建自己的實現。 這篇文章是關於 C# 中的 HttpClient 的。 您還可以在本頁的下載部分找到完整的代碼和項目。
問題是並非所有服務器都支持部分下載。 因此,可以使用 NuGet 包來處理異常,例如: https ://www.nuget.org/packages/downloader 或https://www.nuget.org/packages/Shard.DonwloadLibrary 。 這些庫將處理塊並將它們轉換回可讀文件或流。
下載器:
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);
或下載庫:
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;
我希望我能幫上忙!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.