在我对下面这段多线程 下载代码进行测试时,发现下载到的文件都不能正确打开。查看[文件名]-0等文件时,发现文件一开头是正确数据,然而到了一定点后全都是0。请问是什么问题?要怎么解决?源码如下:
public class Download {
private string URL {get;set;}
private string Path {get;set;}
private long Length {get;set;}
private int Threads{get;set;}
private int Changeds = 0;
private bool Stop = false;
public Download(string dURL,string dPath = null,int dThreads = 64) {
URL = dURL;
Path = dPath;
Threads = dThreads;
TestDo();
}
private void TestDo() {
try {
Path = Path==null ? new Uri(URL).Segments[new Uri(URL).Segments.Length-1] : Path;
ServicePointManager.DefaultConnectionLimit = Threads + 1;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.SystemDefault | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
Thread[] downThs = new Thread[Threads+1];
Thread pushThs = new Thread(() => {
try {for (;;) {
if (Changeds == Threads+1) {
var write = new FileStream(Path,FileMode.Create,FileAccess.Write);
for (int i = 0;i < Threads+1;++i) {
var read = new FileStream(Path + "-" + i,FileMode.Open,FileAccess.Read);
byte[] buffer = new byte[read.Length];
read.Read(buffer,0,buffer.Length);
write.Write(buffer,0,buffer.Length);
read.Close();
#if DEBUG
#else
File.Delete(Path + "-" + i);
#endif
Console.WriteLine("文件"+i+"完成合并");
}
write.Close();
break;
} else {
Thread.Sleep(200);
}
}
} catch(Exception) {
throw;
//Changeds = -1;
}
});
Length = WebRequest.Create(URL).GetResponse().ContentLength;
Console.WriteLine("下载开始!文件大小:"+Length);
for (int i = 0;i<downThs.Length-1;++i) {
downThs[i] = new Thread( id => {
try{long start = ((Length - (Length % Threads)) / Threads) * (int)id;
long end = start + ((Length - (Length % Threads)) / Threads) - 1;
Console.WriteLine("下载开始!我的任务是{0}~{1}",start,end);
HttpWebRequest thisdown = (HttpWebRequest)WebRequest.Create(URL);
thisdown.AddRange(start,end);
var readstream = thisdown.GetResponse().GetResponseStream();
var writstream = new FileStream(Path + "-" + ((int)id).ToString(),FileMode.Create,FileAccess.Write);
byte[] buffer = new byte[end - start];
readstream.Read(buffer,0,buffer.Length);
writstream.Write(buffer,0,buffer.Length);
writstream.Flush();
readstream.Close();
writstream.Close();
Console.WriteLine((int)id + "号线程完成下载任务!");
if (Changeds != -1) {
++Changeds;
}
if (Stop) {
File.Delete(Path + "-" + ((int)id).ToString());
}}
catch(Exception) {
Stop = true;
File.Delete(Path + "-" + ((int)id).ToString());
throw;
}
});
}
downThs[downThs.Length-1] = new Thread(id => {
try{long start = Length - (Length % Threads);
long end = Length;
Console.WriteLine("下载开始!我的任务是{0}~{1}",start,end);
HttpWebRequest thisdown = (HttpWebRequest)WebRequest.Create(URL);
thisdown.AddRange(start,end);
var readstream = thisdown.GetResponse().GetResponseStream();
var writstream = new FileStream(Path + "-" + ((int)id).ToString(),FileMode.Create,FileAccess.Write);
byte[] buffer = new byte[end - start];
readstream.Read(buffer,0,buffer.Length);
writstream.Write(buffer,0,buffer.Length);
writstream.Flush();
readstream.Close();
writstream.Close();
Console.WriteLine((int)id + "号线程完成任务!");
if (Changeds != -1) {
++Changeds;
}
if (Stop) {
File.Delete(Path + "-" + ((int)id).ToString());
}}
catch (Exception) {
Changeds = -1;
Stop = true;
throw;
}
});
pushThs.Start();
for (int i = 0;i < downThs.Length;++i) {
downThs[i].Start(i);
}
pushThs.Join();
Console.WriteLine("完成下载!");
} catch (Exception) {
throw;
}
}
}
byte[] buffer = new byte[end - start];
readstream.Read(buffer,0,buffer.Length)
writstream.Write(buffer,0,buffer.Length)
你这代码我看不下去,园子味太重了。我就来说说这3句。别的地方不谈,就这3句就有可能写的都是0
假设 byte[10000] 但read成功了1000,那么剩下9000就都是0对么
所以
stream.Read 是带返回值的,他告诉你他成功读取了多少个
var readcount= readstream.Read(buffer,0,buffer.Length) ;
你先别合并,看下每个分块下载的对不对,不是所有的服务器都支持请求分块下载的。
如果不对,看你后续分块下载是不是有问题
否则就是合并的问题。
优化后的问题内容: 我正在使用下面的代码进行多线程下载的测试。下载完成后,我发现文件无法正确打开。对于下载的文件,例如 [文件名]-0,我注意到文件开头部分包含正确的数据,但是在某个点之后都变成了0。请问这个问题是由什么原因引起的?我应该如何修改源代码以解决这个问题?以下是我的源代码:
// 请在此处插入您的源代码
首先,对于多线程下载可能导致文件损坏的问题,可能出现以下几个原因:
为了解决这个问题,您可以采取以下步骤:
以下是一个示例代码,展示了如何根据上述建议来修改多线程下载的源代码:
using System;
using System.IO;
using System.Net;
using System.Threading;
public class Program
{
static object fileLock = new object();
public static void Main()
{
string url = "http://example.com/file.ext";
int numThreads = 4;
string outputFileName = "output";
// Create threads
Thread[] threads = new Thread[numThreads];
for(int i = 0; i < numThreads; i++)
{
threads[i] = new Thread(() => DownloadFile(url, outputFileName, i));
threads[i].Start();
}
// Wait for all threads to finish
foreach(Thread thread in threads)
{
thread.Join();
}
Console.WriteLine("Download completed");
}
public static void DownloadFile(string url, string outputFileName, int threadId)
{
try
{
// Create unique output filename for each thread
string threadOutputFileName = outputFileName + "-" + threadId;
// Create a web request to download the file
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.AddRange(GetFileRange(threadId, numThreads));
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream responseStream = response.GetResponseStream())
using (FileStream fileStream = new FileStream(threadOutputFileName, FileMode.Create))
{
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
// Lock the file before writing
lock (fileLock)
{
fileStream.Write(buffer, 0, bytesRead);
}
}
}
}
catch(Exception e)
{
Console.WriteLine("Thread {0} failed: {1}", threadId, e.Message);
}
}
public static long[] GetFileRange(int threadId, int numThreads)
{
// Calculate the range each thread should download
long fileSize = GetRemoteFileSize(url);
long startOffset = (fileSize / numThreads) * threadId;
long endOffset = (threadId == numThreads - 1) ? fileSize - 1 : (fileSize / numThreads) * (threadId + 1) - 1;
return new long[] { startOffset, endOffset };
}
public static long GetRemoteFileSize(string url)
{
using (WebClient client = new WebClient())
{
client.OpenRead(url);
return Int64.Parse(client.ResponseHeaders[HttpResponseHeader.ContentLength]);
}
}
}
请确保根据实际情况修改代码中的变量和URL。这段代码通过使用文件锁和线程间的同步机制来保护文件写入,以确保数据的完整性。每个线程下载的文件将被保存在不同的文件中,避免了数据覆盖和错位。同时,通过限制每个线程的下载范围,确保了文件写入的顺序和时序。
请注意,以上代码仅为示例,具体的实现方式可能根据你的需求而有所不同。