为什么可以';t我拆分下载了两个以上的片段

本文关键字:片段 两个 下载 拆分 为什么 | 更新日期: 2023-09-27 18:29:37

我正在开发一个下载器,该下载器能够将一个文件拆分并下载为多个部分,但目前存在一个问题,即如果我尝试两个以上的部分,输出文件就会损坏。我不知道发生了什么,但我认为这可能发生在寻找单个零件尺寸的过程中。

这里有很多代码,所以我决定把它发布到网站外。代码可以在这里找到:Github

非常感谢在这个问题上的任何帮助。非常感谢。

为什么可以';t我拆分下载了两个以上的片段

好吧,所以你的代码中有很多问题,真正的问题不是你把文件分解成两个以上的部分,而是当你把它分解成三个或更多的部分时,它会在代码中暴露出竞争条件。

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;
        }
    }
}