我正在开发一个下载程序而不是能够将文件拆分并下载到多个部分,但是目前存在一个问题,即如果我尝试任何超过2个部分,则输出文件已损坏。我不知道最近会发生什么,但我认为它可能发生在找到单件尺寸的部分。
有相当多的代码,所以我决定将它发布到网站外。代码可以在这里找到:Github
在这个问题上任何帮助都非常感激。谢谢!
答案 0 :(得分:1)
好的,所以你的代码中有很多问题,而真正的问题不在于你将文件分成两个以上的部分,而是当你将它分成3个或更多个部分时它暴露了你的代码中的竞争条件。
1)您正在尝试从打开要追加的文件的多个线程写入同一文件。每次打开文件时,结束都是移动目标。
2)即使在解决了以文件为中心的问题后,GetResponseAsync也会出现死锁,所以我切换到了HttpClient而不是死锁问题的异步工作。
3)应用KISS校长并简化您的代码。它仍然下载.jpg文件的碎片,虽然我认为你做了一个很大的假设,认为这将比在一个请求中下载整个文件更快。我会测试以确保,因为没有文件分块,你的程序会更简单。
4)我还忘了提到的另一件事是你在命令行应用程序中不能有异步入口点,所以我添加了Task.WaitAll调用以修复你可能遇到的死锁问题。 39;吨。请阅读[Why is AsyncContext needed when using async/await with a console application?了解详情。这段代码每次都有效,不会崩溃,你可以做很多你想要的块。你欠我一杯啤酒: - )。
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;
}
}
}