From 799dd1ad8151046c464762802c36dffefb1bd383 Mon Sep 17 00:00:00 2001 From: Colton <54053306+265866@users.noreply.github.com> Date: Wed, 3 Jun 2026 05:47:43 -0700 Subject: [PATCH] Stream asset downloads to avoid OutOfMemoryException on large files (#1540) * Stream asset downloads to avoid OutOfMemoryException on large files ExportAssetDownloader used HttpClient.GetAsync(url, ct), which defaults to HttpCompletionOption.ResponseContentRead and buffers the entire response body into memory before returning. Downloading a large attachment therefore tried to grow a single in-memory buffer to the full file size and threw OutOfMemoryException (HttpContent.LoadIntoBufferAsync -> GrowAndWrite). Pass HttpCompletionOption.ResponseHeadersRead so the body is streamed straight to disk via CopyToAsync in bounded chunks, matching how DiscordClient already issues its requests. * Update ExportAssetDownloader.cs --------- Co-authored-by: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com> --- .../Exporting/ExportAssetDownloader.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/DiscordChatExporter.Core/Exporting/ExportAssetDownloader.cs b/DiscordChatExporter.Core/Exporting/ExportAssetDownloader.cs index eb44d4a9..071616ec 100644 --- a/DiscordChatExporter.Core/Exporting/ExportAssetDownloader.cs +++ b/DiscordChatExporter.Core/Exporting/ExportAssetDownloader.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Net.Http; using System.Security.Cryptography; using System.Text; using System.Threading; @@ -64,7 +65,14 @@ internal partial class ExportAssetDownloader(string workingDirPath, bool reuse) async innerCancellationToken => { // Download the file - using var response = await Http.Client.GetAsync(url, innerCancellationToken); + using var response = await Http.Client.GetAsync( + url, + HttpCompletionOption.ResponseHeadersRead, + innerCancellationToken + ); + + response.EnsureSuccessStatusCode(); + await using var output = File.Create(filePath); await response.Content.CopyToAsync(output, innerCancellationToken); },