我需要为我的文件下载器应用程序实现下载速率限制,并且我已经查看了CodeProject中的ThrottledStream
类,但这不适用于我的情况,因为我必须能够更改下载期间的限制,而不仅仅是开始时的限制。这是我在后台线程中使用的下载方法的一部分:
webResponse = (HttpWebResponse)webRequest.GetResponse();
responseStream = webResponse.GetResponseStream();
responseStream.ReadTimeout = 5000;
downloadCache = new MemoryStream(this.MaxCacheSize);
byte[] downloadBuffer = new byte[this.BufferSize];
int bytesSize = 0;
CachedSize = 0;
int receivedBufferCount = 0;
while (true)
{
bytesSize = responseStream.Read(downloadBuffer, 0, downloadBuffer.Length);
if (this.Status != DownloadStatus.Downloading || bytesSize == 0
|| this.MaxCacheSize < CachedSize + bytesSize)
{
WriteCacheToFile(downloadCache, CachedSize);
this.DownloadedSize += CachedSize;
downloadCache.Seek(0, SeekOrigin.Begin);
CachedSize = 0;
if (this.Status != DownloadStatus.Downloading || bytesSize == 0)
break;
}
downloadCache.Write(downloadBuffer, 0, bytesSize);
CachedSize += bytesSize;
receivedBufferCount++;
if (receivedBufferCount == this.BufferCountPerNotification)
{
this.RaiseDownloadProgressChanged();
receivedBufferCount = 0;
}
}
我也见过有人使用Thread.Sleep()或Thread.Wait(),但这是个好主意吗?你有什么建议我可以在这个循环中做到这一点吗?
答案 0 :(得分:1)
我使用此代码从服务器下载文件可能对您有帮助...
private bool DownLoadFile(string pstrFileName, string pstrFilePath, long plngFileSize)
{
try
{
string strNewFileSize = CalcFileSize(plngFileSize);
int numIterations = 0; // this is used with a modulus of the sampleInterval to check if the chunk size should be adjusted. it is started at 1 so that the first check will be skipped because it may involve an initial delay in connecting to the web service
Offset = 0;
long webConfigSetting = this.mobjService.GetMaxRequestLength();
this.MaxRequestLength = Math.Max(1, (webConfigSetting * 1024) - (2 * 1024)); // set the max buffer size to slightly less than the request length to allow for SOAP message headers etc.
if (File.Exists(pstrFilePath))
{
Offset = new FileInfo(pstrFilePath).Length;
if (Offset == plngFileSize)
Offset = 0;
//File.Delete(pstrFilePath);
}
if (Offset == 0 && !File.Exists(pstrFilePath)) // create a new empty file
File.Create(pstrFilePath).Close();
// open a file stream for the file we will write to in the start-up folder
lblFileName.Text = pstrFileName.Substring(0, pstrFileName.LastIndexOf(".")).Replace("&", "&&");
using (FileStream fs = new FileStream(pstrFilePath, FileMode.OpenOrCreate, FileAccess.Write))
{
pbrSummary.Maximum = (int)plngFileSize;
fs.Seek(Offset, SeekOrigin.Begin);
// download the chunks from the web service one by one, until all the bytes have been read, meaning the entire file has been downloaded.
while (Offset < plngFileSize)
{
int currentIntervalMod = numIterations % this.ChunkSizeSampleInterval;
if (currentIntervalMod == 0)
StartTime = DateTime.Now; // used to calculate the time taken to transfer the first 5 chunks
else if (currentIntervalMod == 1)
CalcAndSetChunkSize(plngFileSize);
try
{
// although the DownloadChunk returns a byte[], it is actually sent using MTOM because of the configuration settings.
byte[] Buffer = mobjService.DownloadChunk(pstrFileName, Offset, ChunkSize);
fs.Write(Buffer, 0, Buffer.Length);
Offset += Buffer.Length; // save the offset position for resume
}
catch (Exception ex)
{
if (ex.Message.Contains("File not found") || NumRetries++ >= MaxRetries) // too many retries, bail out
{
fs.Close();
return false;
//throw new Exception("Error occurred during upload, too many retries.\r\n" + ex.Message);
}
}
numIterations++;
//----------------------- Code Commented ------------------------
//lblDownload.Text = CalcFileSize(Offset) + " of " + strNewFileSize;
//lblRate.Text = CalcFileSize(ChunkSize)+"/sec";
////pbrSummary.Value = (int)Offset;
////if ((int)Offset <= pbrSummary.Maximum)
//// pbrSummary.Value = (int)Offset;
////else
//// pbrSummary.Value = pbrSummary.Maximum;
//------------------------------------------------------------------
}
}
return true;
}
catch (Exception Exc)
{
throw (Exc);
}
}
private void CalcAndSetChunkSize(long plngFileSize)
{
/* chunk size calculation is defined as follows
* in the examples below, the preferred transfer time is 1500ms, taking one sample.
*
* Example 1 Example 2
* Initial size = 16384 bytes (16k) 16384
* Transfer time for 1 chunk = 800ms 2000 ms
* Average throughput / ms = 16384b / 800ms = 20.48 b/ms 16384 / 2000 = 8.192 b/ms
* How many bytes in 1500ms? = 20.48 * 1500 = 30720 bytes 8.192 * 1500 = 12228 bytes
* New chunksize = 30720 bytes (speed up) 12228 bytes (slow down from original chunk size)
*/
double transferTime = DateTime.Now.Subtract(this.StartTime).TotalMilliseconds;
double averageBytesPerMilliSec = this.ChunkSize / transferTime;
double preferredChunkSize = averageBytesPerMilliSec * this.PreferredTransferDuration;
this.ChunkSize = (int)Math.Min(this.MaxRequestLength, Math.Max(4 * 1024, preferredChunkSize)) * 10; // set the chunk size so that it takes 1500ms per chunk (estimate), not less than 4Kb and not greater than 4mb // (note 4096Kb sometimes causes problems, probably due to the IIS max request size limit, choosing a slightly smaller max size of 4 million bytes seems to work nicely)
//string statusMessage = String.Format("Chunk size: {0}{1}", CalcFileSize(this.ChunkSize), (this.ChunkSize == this.MaxRequestLength) ? " (max)" : "");
}