From 0a7d1cf90647941492ceacf393645567f1b6dffe Mon Sep 17 00:00:00 2001 From: Damien Ostler Date: Sun, 12 Mar 2023 04:40:25 -0400 Subject: [PATCH 1/2] Fixed with comments explaining --- Program.cs | 76 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 22 deletions(-) diff --git a/Program.cs b/Program.cs index a989e6c..ba3881a 100644 --- a/Program.cs +++ b/Program.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Logging; using System; +using System.Collections.Concurrent; using System.Drawing; using System.Globalization; using System.IO; @@ -93,44 +94,75 @@ static void SelfFinalize(string fileName, string Fcontenttype, string Link) { long size = Link_Request.File_Size(Link); - DownloadChunk[] chunks = GetChunks(4 , size); + + ConcurrentBag chunks = GetChunks(4 , size); + + //https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/introduction-to-plinq + //Parallel operations using LINQ + var processedChunks = chunks.AsParallel().Select(x => Download(x, Link)).ToList(); + + // DAMIEN :Again when you are using a disposable object you need to use a using statement so it gets rid of it properly. Another + //important thing about the using statement is that when it exists the function or if there is an exception it will + //automatically close the stream, or dispose of the object. + using (FileStream fs = new("C:\\Users\\DamienOstler\\Downloads\\" + fileName + Fcontenttype, FileMode.Create)) + { + var offset = 0; + //Sort by starting so that the one starting at 0 goes first and so on + foreach (DownloadChunk chunk in processedChunks.OrderBy(x=>x.Start)) + { + //You should be using the size element since u already have it here. + fs.Write(chunk.Data, 0, (int)chunk.Size); + offset += chunk.Data.Length; + } + } + - Parallel.ForEach(chunks, (chunk) => + } + + //NEW METHOD FOR DOWNLOADING THE CHUNKS + private static DownloadChunk Download(DownloadChunk chunk,string Link) + { + HttpWebRequest request = WebRequest.CreateHttp(Link); + request.AddRange(chunk.Start, chunk.End); + // DAMIEN :When you are using a class that implements a idisposable interface thats when you use a using statement. And to + //use it properly you must wrap it as I do below, and then either only have one statement after all of them + //indented like you do with a if statement with no {, OR have a opening and closing { } where all the code that + //needs that disposable will run. Whenever it exits that codeblock {}, it will automatically call .dispose() on + //the disposable. In this case its closing the stream. So the reason everything wasnt working was that all the + //the streams were immediatly being closed. + using (WebResponse response = request.GetResponse()) + using (Stream responseStream = response.GetResponseStream()) + using (MemoryStream memoryStream = new MemoryStream()) { - HttpWebRequest request = WebRequest.CreateHttp(Link); - request.AddRange(chunk.Start, chunk.End); - using WebResponse response = request.GetResponse(); - using Stream responseStream = response.GetResponseStream(); byte[] buffer = new byte[1024]; int bytesRead; - using MemoryStream memoryStream = new MemoryStream(); while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0) { memoryStream.Write(buffer, 0, bytesRead); } + // DAMIEN : You need to set the posistion of the memory stream to 0 otherwise its never going to actually copy the contents over + //since it will not start at the beggining of the the stream but at the + memoryStream.Position = 0; chunk.Data = memoryStream.ToArray(); - }); - - using FileStream fs = new("C:\\Users\\Derpy\\Downloads\\" + fileName + Fcontenttype, FileMode.Create); - foreach (DownloadChunk chunk in chunks) - { - fs.Write(chunk.Data, 0, chunk.Data.Length); + return chunk; } - - } - private static DownloadChunk[] GetChunks(int parts, long totalsize) + + //This method had to be changed to return a concurrent bag so that it is thread safe since you are doing a parallel foreach. + private static ConcurrentBag GetChunks(int parts, long totalsize) { - DownloadChunk[] chunks = new DownloadChunk[parts]; + ConcurrentBag chunks = new ConcurrentBag(); long chunksize = totalsize / parts; - for (int i = 0; i < chunks.Length; i++) + // DAMIEN : Changed this loop to use the parts integer since it is the size already. + for (int i = 0; i < parts; i++) { - chunks[i] = new() + chunks.Add(new() { - Start = i == 0 ? 0 : chunksize = i + 1, - End = i == 0 ? chunksize : i == chunks.Length - 1 ? totalsize : chunksize * i + chunksize - }; + //The math on this was wrong + Start = i == 0 ? 0 : chunksize *( i)+1, + End = i == 0 ? chunksize : chunksize * (i + 1) + }); } return chunks; } From a81c2e4f6cfa484248e91388b799105a7438b90d Mon Sep 17 00:00:00 2001 From: Damien Ostler Date: Sun, 12 Mar 2023 04:41:09 -0400 Subject: [PATCH 2/2] Update Program.cs --- Program.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Program.cs b/Program.cs index ba3881a..18a64f3 100644 --- a/Program.cs +++ b/Program.cs @@ -95,6 +95,8 @@ static void SelfFinalize(string fileName, string Fcontenttype, string Link) long size = Link_Request.File_Size(Link); + //When the parallel methods were added so were concurrent collections and other features. Concurrent collections + //are multi thread safe. ConcurrentBag chunks = GetChunks(4 , size); //https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/introduction-to-plinq