-
Notifications
You must be signed in to change notification settings - Fork 555
Description
Apple platform
iOS
Framework version
net8.0-*
Affected platform version
.NET 8.0.302, VS Code 1.94.2, .NET MAUI extension v1.4.36
Description
When attempting to upload a file, it appears that the entirety of the file is first copied into memory, then uploaded. This causes two problems.
- Large file uploads cause devices with limited memory to run out of memory.
- Attempting to track progress for the actual upload doesn't work, since it jumps to 100% quickly (the copy to memory), then reports no progress for the actual upload, which takes much longer than the copy to memory.
The issue here seems to point to similar problems, but the discussion centered around whether the content was coming in as a MemoryStream or FileStream. This is a FileStream, which I would not expect to enter memory all at once, since it should be streamed from disk to be uploaded in chunks.
Steps to Reproduce
- Follow the steps here to create a new MAUI application (MAUI was used for simplicity, but the code in question appears to be generic to xamarin-macios).
- Write the code below. In summary, it's simply taking a local file (large enough to observe a delay when uploading) and uploading it to an endpoint that will accept it. (Note that the specifics of our endpoint are removed for privacy.)
- Observe that the
ProgressMessageHandlerreports progress in chunks, as expected, but the progress is much too fast to be the actual upload. They all happen within the same second or two. - Then observe that the upload doesn't actually finish until several seconds later. See Relevant Log Output section below.
- We would expect to see the
ProgressMessageHandlertrack the actual upload, instead of presumably tracking the copy to memory.
using System.Net.Http.Handlers;
using System.Net.Http.Headers;
namespace MyMauiApp
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private async void OnTestBtnClicked(object sender, EventArgs e)
{
await PerformUploadAsync();
}
private static async Task PerformUploadAsync()
{
ProgressMessageHandler progressMsgHander = new ProgressMessageHandler();
progressMsgHander.HttpSendProgress += (object? sender, HttpProgressEventArgs e) =>
{
Console.WriteLine($"{DateTime.Now}: Sent {e.BytesTransferred}, Percent Complete: {e.ProgressPercentage}%");
};
progressMsgHander.InnerHandler = new HttpClientHandler();
HttpClient httpClient = new HttpClient(progressMsgHander);
using MultipartFormDataContent formContent = new MultipartFormDataContent("mycoolboundary");
if (formContent.Headers.ContentType != null)
formContent.Headers.ContentType.MediaType = "multipart/form-data";
using FileStream fs = new FileStream("test.zip", FileMode.Open);
using StreamContent streamContent = new StreamContent(fs);
string token = "<redacted>";
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("multipart/form-data"));
streamContent.Headers.ContentLength = fs.Length;
formContent.Add(streamContent, "test", fs.Name);
HttpResponseMessage response = await httpClient.PostAsync("<redacted>", formContent);
response.EnsureSuccessStatusCode();
Console.WriteLine($"{DateTime.Now}: Done Uploading");
}
}
}
Did you find any workaround?
Adding a breakpoint to the HttpSendProgress event handler allows us to see NSUrlSessionHandler.cs in the call stack. That file appears to check MaxInputInMemory (line 503 currently). If we create a platform-specific shim to set this value to something much smaller than long.MaxValue, the behavior is as expected.
It doesn't seem possible that this workaround would be expected/required for what seems to be a common use case. Is that expectation correct?
Relevant log output
2024-10-28 10:19:10.982692-0400 MyMauiApp[99543:3985957] 10/28/2024 10:19:10 AM: Sent 18, Percent Complete: 0%
2024-10-28 10:19:10.983369-0400 MyMauiApp[99543:3985957] 10/28/2024 10:19:10 AM: Sent 515, Percent Complete: 0%
2024-10-28 10:19:11.002468-0400 MyMauiApp[99543:3986541] 10/28/2024 10:19:11 AM: Sent 131587, Percent Complete: 0%
2024-10-28 10:19:11.004298-0400 MyMauiApp[99543:3986543] 10/28/2024 10:19:11 AM: Sent 262659, Percent Complete: 1%
2024-10-28 10:19:11.005538-0400 MyMauiApp[99543:3986544] 10/28/2024 10:19:11 AM: Sent 393731, Percent Complete: 2%
2024-10-28 10:19:11.005892-0400 MyMauiApp[99543:3986543] 10/28/2024 10:19:11 AM: Sent 524803, Percent Complete: 3%
...
2024-10-28 10:19:11.044001-0400 MyMauiApp[99543:3986541] 10/28/2024 10:19:11 AM: Sent 17302019, Percent Complete: 99%
2024-10-28 10:19:11.044295-0400 MyMauiApp[99543:3986541] 10/28/2024 10:19:11 AM: Sent 17363459, Percent Complete: 99%
2024-10-28 10:19:11.045475-0400 MyMauiApp[99543:3986541] 10/28/2024 10:19:11 AM: Sent 17363481, Percent Complete: 100%
2024-10-28 10:19:27.667022-0400 MyMauiApp[99543:3985957] 10/28/2024 10:19:27 AM: Done Uploading