dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.28k stars 9.96k forks source link

FileUpload: Did not receive any data in the allotted time. #38842

Closed Liero closed 1 month ago

Liero commented 2 years ago

Describe the bug

Not sure if this is a bug but I haven't found any documentation either.

IBrowserFile.ReportFile!.OpenReadStream(maxFileSize).CopyToAsync(fileStream); sometimes throws

System.TimeoutException: Did not receive any data in the allotted time.

To Reproduce

I haven't found a reliable way to reproduce the issue, since it happens only sometimes and have no idea what is causing it.

Here is the code:

IBrowserFile _selectedFile;

void OnSelectedFile(InputFileChangeEventArgs args)
{
    _selectedFile = args.File;
}

private async Task UploadMethod()
{
    string basePath = @"\\fileshare\folder";
    string relativePath = @"{MyFolder\DateTime.Now:dd.MM.yyyy}.pdf";
    string fullPath = Path.Combine(basePath, relativePath);
    using FileStream fs = new FileStream(fullPath, FileMode.Create);

    const long maxFileSize = 5000000; // 5 MB
    if (_selectedFile.Size > maxFileSize)
    {
        Toasts.ShowError("Chyba pri ukladaní. Súbor OTDR má viac ako 5 MB.", "OTDR Report");
    }
    else
    {
        _selectedFile.OpenReadStream(maxFileSize).CopyToAsync(fs);
    }
}

Exceptions (if any)

System.TimeoutException: Did not receive any data in the allotted time. ``` [ { "method":"System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw", "level":0, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"System.IO.Pipelines.Pipe.GetReadResult", "level":1, "line":0, "assembly":"System.IO.Pipelines, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" }, { "method":"System.IO.Pipelines.Pipe.GetReadAsyncResult", "level":2, "line":0, "assembly":"System.IO.Pipelines, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" }, { "method":"System.IO.Pipelines.PipeReaderStream+d__30.MoveNext", "level":3, "line":0, "assembly":"System.IO.Pipelines, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" }, { "method":"System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw", "level":4, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess", "level":5, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification", "level":6, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"Microsoft.AspNetCore.Components.Server.Circuits.RemoteJSDataStream+d__36.MoveNext", "level":7, "line":0, "assembly":"Microsoft.AspNetCore.Components.Server, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60" }, { "method":"System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw", "level":8, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess", "level":9, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification", "level":10, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"Microsoft.AspNetCore.Components.Forms.BrowserFileStream+d__30.MoveNext", "level":11, "line":0, "assembly":"Microsoft.AspNetCore.Components.Web, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60" }, { "method":"System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw", "level":12, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess", "level":13, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification", "level":14, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"Microsoft.AspNetCore.Components.Forms.BrowserFileStream+d__28.MoveNext", "level":15, "line":0, "assembly":"Microsoft.AspNetCore.Components.Web, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60" }, { "method":"System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw", "level":16, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess", "level":17, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification", "level":18, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"System.IO.Stream+<g__Core|29_0>d.MoveNext", "level":19, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw", "level":20, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess", "level":21, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification", "level":22, "line":0, "assembly":"System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" }, { "method":"MyApp.Pages.MyPage+d__54.MoveNext", "level":23, "line":348, "assembly":"MyApp, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null", "fileName":"D:\\a\\1\\s\\src\\app\\Pages\\MyPage.razor.cs" } ] ```

Further technical details

dotnet --info Output ``` .NET SDK (reflecting any global.json): Version: 6.0.100 Commit: 9e8b04bbff Runtime Environment: OS Name: Windows OS Version: 10.0.22000 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\6.0.100\ Host (useful for support): Version: 6.0.0 Commit: 4822e3c3aa ```
frutasAka commented 1 year ago

I have the same issue and the workarounds didnt work. Any other solution?

yandoldonov commented 1 year ago

i am having the same issue calling calling this combination:

await using FileStream fs = new(path, FileMode.Create);
await file.OpenReadStream(maxFileSize).CopyToAsync(fs);

I am also using Autofac in my project.

Stack Trace:

   at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
   at System.IO.Pipelines.Pipe.GetReadAsyncResult()
   at System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable`1.ConfiguredValueTaskAwaiter.GetResult()
   at System.IO.Pipelines.PipeReaderStream.<ReadAsyncInternal>d__30.MoveNext()
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Components.Server.Circuits.RemoteJSDataStream.<ReadAsync>d__36.MoveNext()
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Components.Forms.BrowserFileStream.<CopyFileDataIntoBuffer>d__30.MoveNext()
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Components.Forms.BrowserFileStream.<ReadAsync>d__28.MoveNext()
   at System.IO.Stream.<<CopyToAsync>g__Core|27_0>d.MoveNext()

I can confirm the work-around proposed by @haas-daniel - did help resolve the problem.

builder.Services
    .AddServerSideBlazor()
    .AddHubOptions(opt =>
    {
        opt.DisableImplicitFromServicesParameters = true;
    });
frutasAka commented 1 year ago

I have the same issue and the workarounds didnt work. Any other solution?

Since I was using ABP, I need to use @haas-daniel solution this way

Configure<HubOptions>(options => { options.DisableImplicitFromServicesParameters = true; });

ColtonHoksbergen commented 1 year ago

I'm not having any of the error messages unless I'm debugging and breakpoints delay my read, but my ReadAsync() always caps out at 32256 bytes, resulting in a corrupted image if the image was larger than this. Anyone have a solution for this?

haas-daniel commented 1 year ago

I'm not having any of the error messages unless I'm debugging and breakpoints delay my read, but my ReadAsync() always caps out at 32256 bytes, resulting in a corrupted image if the image was larger than this. Anyone have a solution for this?

I think your problem is the SignalR message size limit @ColtonHoksbergen. This defaults to 32KB.

https://learn.microsoft.com/en-us/aspnet/core/blazor/file-uploads?view=aspnetcore-7.0&pivots=server#signalr-message-size-limit

ColtonHoksbergen commented 1 year ago

I'm not having any of the error messages unless I'm debugging and breakpoints delay my read, but my ReadAsync() always caps out at 32256 bytes, resulting in a corrupted image if the image was larger than this. Anyone have a solution for this?

I think your problem is the SignalR message size limit @ColtonHoksbergen. This defaults to 32KB.

https://learn.microsoft.com/en-us/aspnet/core/blazor/file-uploads?view=aspnetcore-7.0&pivots=server#signalr-message-size-limit

Thank you very much, that was the exact issue. I know this is getting unrelated, but while my stream can now accept larger file types, the position is always 32256 regardless of if the stream is larger, and it is not seekable. Do you know what I might be doing wrong?

alpisala02 commented 1 year ago

Configure(options => { options.DisableImplicitFromServicesParameters = true; });

Thank you so much! It worked perfectly.

SammyGCh commented 1 year ago

Hi, I faced to this issue and the @12evenant answer resolved it.

Liero commented 1 year ago

Please, upvote the original issue, so that it will be prioritized

arielmoraes commented 1 year ago

I'm also facing that issue, but in my case I'm reading and writing manually to another stream to have a progress callback.

When the streaming operation finishes with success there is no problem, but if any Exception is thrown the app hangs.

The method responsible for the streaming operation is asynchronous, so the root cause might be because the OpenReadStream method is not closing the stream after a failure in the current async flow.

If I close the stream explicitally in a try-catch block the problem doesn't happen.

try
{
    await fileStore.SaveOriginFile(new SaveFileRequest(
        incomingFileStream,
        e.File.Name,
        (progress) =>
        {
            uploadProgressValue = Convert.ToInt32(progress / e.File.Size * 100);
            StateHasChanged();
        }));

    success = true;
}
catch (Exception ex)
{
    incomingFileStream.Close();
    errorMessage = "There was an error while uploading the selected file.";
}
philnwoha commented 1 year ago

await stream.ReadAsync(fileBytes);

Hi All, In my case I was able to solve this by closing the stream.

    private async Task<bool> Upload(IBrowserFile file)
    {
        var fileBytes = new byte[file.Size];
        var stream = file.OpenReadStream();
        try
        {
            await stream.ReadAsync(fileBytes);
            //do something
            return true;
        }
        catch (Exception ex)
        {
            //do something
            return false;
        }
        finally
        {
            stream.Close();
        }
    }

This worked for me. Thanks @12evenant

ghost commented 12 months ago

Thanks for contacting us.

We're moving this issue to the .NET 9 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

dadazxy commented 11 months ago

我有同样的问题,并且解决方法不起作用。还有其他解决方案吗?

有解决吗

ghost commented 9 months ago

Thanks for contacting us.

We're moving this issue to the .NET 9 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

AdamJonsson commented 8 months ago

These workarounds worked for me: https://github.com/dotnet/aspnetcore/issues/47301#issuecomment-1520676385

nblumhardt commented 7 months ago

DisableImplicitFromServicesParameters should probably be the default here. Otherwise, SignalR/the framework is making assumptions about container behavior that are only valid for the MEDI container.

Autofac (right back as far as the beginning of .NET Core) has always regarded IEnumerable<T> or T[] as resolvable, even if no implementations exist, because IEnumerable<T> is a zero-to-many dependency. Several other containers follow this model IIRC.

Unfortunate that this wasn't spotted before the original release of this API, not sure what can be done now, but it's little friction points like this that erode the value in having multiple independent implementations out there :-)

For example, if the default behavior can't be changed, could the implicit-from-services mechanism internally apply a filter to decide what types constitute valid "services"? Or ignore registered services that are enumerable and empty?

nblumhardt commented 7 months ago

How about - leave the default intact, but skip implicit injection of services that are arrays of structs, or generic collection types/interfaces where the closing type is a struct?

I'm not sure how many scenarios apart from byte[] are actually interesting, but I also can't think of many cases where collections of structs would come from DI. But, leaving the attribute as an explicit opt-in would cover anything we're missing.

Could be a bit tedious to get right, but I'm game to try assembling a PR if it'd help.

erosnemesis commented 5 months ago

await stream.ReadAsync(fileBytes);

Hi All, In my case I was able to solve this by closing the stream.

    private async Task<bool> Upload(IBrowserFile file)
    {
        var fileBytes = new byte[file.Size];
        var stream = file.OpenReadStream();
        try
        {
            await stream.ReadAsync(fileBytes);
            //do something
            return true;
        }
        catch (Exception ex)
        {
            //do something
            return false;
        }
        finally
        {
            stream.Close();
        }
    }

This worked for me. Thanks @12evenant

Can confirm that closing the steam solves the issue.

umartechboy commented 4 months ago

I can confirm this too. Closing the stream solves a similar issue in my project too.

Scenario: MudBlazor installed FileChanged is called by MudBlazor file selector which is placed inside a MudDialog, reading the stream into buffer works. The rest of the process triggers too, around 3-4 seconds later, the UI Updates from the code become unresponsive. 20-30 seconds later, Timeout exception is thrown.

It was so frustrating to find the root cause because all my tests indicated problems with MudBlazor MudDialog or the CPU intensive work I was doing on the loaded data (WhisperNet. I had to change the library from Whisper.net to WhisperNet, now I feel sorry for blaming Whisper.net)

Resolve Close the IBrowserFile stream right after read is finished!! A dumb thing to forget, I'd say. Only a desktop app developer can make the mistake.

DontEatRice commented 3 months ago

Any news on this? Still happening when we try to upload multiple files. We do not use AutoFac.
Scenario: server-side Blazor tries to upload more than one file to an external service using HttpClient (RestSharp as wrapper) using streams. When there is more than one file System.TimeoutException: Did not receive any data in the allotted time. is thrown. Closing streams is pointless when they are being read in parallel

TheAnachronism commented 2 months ago

I run into the same issue when doing things parallel. Although it doesn't actually happen all the time. I haven't been able to figure out which conditions actually cause the exception to happen, which makes it all the more difficult trying to find it. And like @DontEatRice has mentioned, closing the streams doesn't make a difference, as nothing is received in the first place.

MackinnonBuck commented 1 month ago

Any news on this? Still happening when we try to upload multiple files. We do not use AutoFac. Scenario: server-side Blazor tries to upload more than one file to an external service using HttpClient (RestSharp as wrapper) using streams. When there is more than one file System.TimeoutException: Did not receive any data in the allotted time. is thrown. Closing streams is pointless when they are being read in parallel

@DontEatRice, would you be able to provide us with a repro that demonstrates the bug? The repro we've seen so far wasn't reading the stream to completion (see https://github.com/dotnet/aspnetcore/issues/38842#issuecomment-1151415351), and others using Autofac mentioned that https://github.com/dotnet/aspnetcore/issues/38842#issuecomment-1342540950 resolved the issue for them.

A repro that demonstrates a framework bug would be very valuable, so if you (or someone else) can provide a minimal repro that doesn't use third-party dependencies and is hosted as a public GitHub repository, that would be much appreciated. Thanks!

DontEatRice commented 1 month ago

@MackinnonBuck

I have created the repo that demonstrates this bug: https://github.com/DontEatRice/BlazorFileUploadIssue This repo has no third-party dependencies. I have also included a simple api service that saves files to disk.

First run the Web Api in the Api folder.

Go to the BlazorApp folder and run it too. On the left panel you will see the Upload Files tab. You will find sample files in the BlazorApp/Data folder. They have been created using the fsutil command. Select all of them to see the bug. Of course you can use your own files, but the requirement is that they must be at least 2MB each (approximately).

After trying to upload them, Blazor will throw an exception after some time. You can upload any single file and it will work.

In net7.0 the exception stack trace was longer, something happened, and in net8.0 it is quite short.

Please message me if you have any questions.

MackinnonBuck commented 1 month ago

Thank you for the repro, @DontEatRice! It looks like this is exactly the same issue as #47301, and you can see a detailed explanation here that shows what's going on.

The code in your repro can be modified slightly to make this work:

+var memoryStream = new MemoryStream();
+var browserFileStream = file.OpenReadStream(maxAllowedSize: 15_000_000);
+await browserFileStream.CopyToAsync(memoryStream);
+memoryStream.Position = 0;

-var fileContent = new StreamContent(file.OpenReadStream(long.MaxValue));
+var fileContent = new StreamContent(memoryStream);
fileContent.Headers.ContentType = new MediaTypeHeaderValue(file.ContentType);
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = "\"Files\"", FileName = $"\"{file.Name}\"" };

content.Add(fileContent);

Within this single GitHub issue, we've seen multiple causes of this exception:

So, it's not clear to me whether this issue tracks a unique framework bug. There's a lot of community activity here, but the root cause of the problem seems to vary widely. This probably indicates we should update our documentation to help developers diagnose this issue better.

@guardrex, would you be able to add a section to the file upload docs that explain some of the common causes of this exception?

Also, it looks like the sample in this section might be susceptible to this problem. If that's the case, could we update that sample to take an approach similar to what I wrote above (in the diff)? Thanks!

guardrex commented 1 month ago

@MackinnonBuck ... This one came in, too, which I was waiting to discuss around 🦃 Day. I'll mention this issue to that dev.

https://github.com/dotnet/AspNetCore.Docs/issues/33272

TheAnachronism commented 1 month ago

I have a question about the code example. The documentation mentions that files shouldn't be copied in to a MemoryStream, but should be directly used for whatever happens to them next. (https://learn.microsoft.com/en-gb/aspnet/core/blazor/file-uploads?view=aspnetcore-8.0#file-uploads)

I've done this for my implementation and passed the stream from OpenReadStream to the PutAsync method of a MinioClient for an S3 upload.

MackinnonBuck commented 1 month ago

The documentation mentions that files shouldn't be copied in to a MemoryStream, but should be directly used for whatever happens to them next.

That's true in most cases, but in the repro provided by @DontEatRice, the reading of each stream is controlled by the implementation of MultipartContent, which only consumes each stream just before the request gets made. To avoid the deadlock, each stream needs to be completely consumed before the next call to IBrowserFile.OpenReadStream(). If you want to avoid reading the entire stream into memory, you could limit each request to only transmitting a single file. If you do decide to read into a MemoryStream, ensure that you provide an appropriate maxAllowedSize argument in the call to IBrowserFile.OpenReadStream().

Note that this workaround won't be required:

TheAnachronism commented 1 month ago

Ah, thank you. Guess I'll change this until that issue is resolved.

MackinnonBuck commented 1 month ago

I'm going to close this out now:

If others are experiencing this issue and the root cause is not one of those listed in this comment, please open a new issue with detailed reproduction steps and a minimal project that can be used to reproduce the issue (hosted as a public GitHub repository, not a zip file). Thanks!