为什么可以';t我拆分下载了两个以上的片段
本文关键字:片段 两个 下载 拆分 为什么 | 更新日期: 2023-09-27 18:29:37
我正在开发一个下载器,该下载器能够将一个文件拆分并下载为多个部分,但目前存在一个问题,即如果我尝试两个以上的部分,输出文件就会损坏。我不知道发生了什么,但我认为这可能发生在寻找单个零件尺寸的过程中。
这里有很多代码,所以我决定把它发布到网站外。代码可以在这里找到:Github
非常感谢在这个问题上的任何帮助。非常感谢。
好吧,所以你的代码中有很多问题,真正的问题不是你把文件分解成两个以上的部分,而是当你把它分解成三个或更多的部分时,它会在代码中暴露出竞争条件。
1) 您正试图从打开文件进行追加的多个线程写入同一文件。每次打开文件时,结尾都是一个移动的目标。
2) 即使在修复了以文件为中心的问题之后,GetResponseAsync也会死锁,所以我改用HttpClient,它以异步方式工作,没有死锁问题。
3) 应用了KISS原理并简化了代码。它仍然会下载.jpg文件的片段,尽管我认为你在做一个很大的假设,认为这将比在一个请求中下载整个文件更快。我会测试以确保,因为如果没有文件分块,你的程序会简单得多。
4) 还有一件事我忘了提,那就是命令行应用程序中不能有异步入口点,所以我添加了Task.WaitAll调用来修复如果不这样做可能出现的死锁。阅读[为什么在控制台应用程序中使用async/await时需要AsyncContext?了解更多详细信息。
这段代码每次都能工作,不会崩溃,而且您可以随心所欲地执行块。你欠我一杯啤酒:-)。
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace OctaneDownloadEngine
{
class Program
{
static void Main()
{
try
{
// have to use this because you can't have async entrypoint
Task.WaitAll(SplitDownload("http://www.hdwallpapers.in/walls/tree_snake_hd-wide.jpg", @"c:'temp'output.jpg"));
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
throw;
}
Console.ReadLine();
}
public static async Task<string> SplitDownload(string URL, string OUT)
{
var responseLength = WebRequest.Create(URL).GetResponse().ContentLength;
var partSize = (long)Math.Floor(responseLength / 4.00);
Console.WriteLine(responseLength.ToString(CultureInfo.InvariantCulture) + " TOTAL SIZE");
Console.WriteLine(partSize.ToString(CultureInfo.InvariantCulture) + " PART SIZE" + "'n");
var previous = 0;
var fs = new FileStream(OUT, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, (int)partSize);
try
{
fs.SetLength(responseLength);
List<Tuple<Task<byte[]>, int, int>> asyncTasks = new List<Tuple<Task<byte[]>, int, int>>();
for (var i = (int)partSize; i <= responseLength + partSize; i = (i + (int)partSize) + 1)
{
var previous2 = previous;
var i2 = i;
// GetResponseAsync deadlocks for some reason so switched to HttpClient instead
HttpClient client = new HttpClient() { MaxResponseContentBufferSize = 1000000 };
client.DefaultRequestHeaders.Range = new RangeHeaderValue(previous2, i2);
byte[] urlContents = await client.GetByteArrayAsync(URL);
// start each download task and keep track of them for later
Console.WriteLine("start {0},{1}", previous2, i2);
var downloadTask = client.GetByteArrayAsync(URL);
asyncTasks.Add(new Tuple<Task<byte[]>, int, int>(downloadTask, previous2, i2));
previous = i2;
}
// now that all the downloads are started, we can await the results
// loop through looking for a completed task in case they complete out of order
while (asyncTasks.Count > 0)
{
Tuple<Task<byte[]>, int, int> completedTask = null;
foreach (var task in asyncTasks)
{
// as each task completes write the data to the file
if (task.Item1.IsCompleted)
{
Console.WriteLine("await {0},{1}", task.Item2, task.Item3);
var array = await task.Item1;
Console.WriteLine("write to file {0},{1}", task.Item2, task.Item3);
fs.Position = task.Item2;
foreach (byte x in array)
{
if (fs.Position != task.Item3)
{
fs.WriteByte(x);
}
}
completedTask = task;
break;
}
}
asyncTasks.Remove(completedTask);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
Console.WriteLine("close file");
fs.Close();
}
return OUT;
}
}
}