-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ReadableStream.ReadAsync doesn't respect buffer lengths #3
Comments
Hey @sschoener, Thank you so much for your interest in the library. You have some very interesting points. I had not thought of this use case before as I have primarily seen people using the library to stream the entirety of a file. I've read The documentation for the ReadAsync method and as you state it should either return some number equal to or less than the buffer size. And if no bytes were read, then return It is a good idea to use an internal buffer to save the rest of any read bytes so that they can be consumed later. I see it as a pretty straightforward solution but it also hides some behavior and doesn't make it directly obvious how many bytes were actually read from the stream which could make a user assume that the stream was in another state. So before I decide on a solution I will check out whether it would be possible to read the stream in the desired way using a ReadableStreamBYOBReader as that might lead to a more transparent solution. |
I have actually tried both now :) Find both versions below. The first needs the BYOB reader, the second the regular one. I have not included any of the members that aren't relevant. This is the version with the BYOB reader:
where I'm using this javascript:
Note here that the BYOB reader requires you to return the buffer that its Alternatively, here is the approach that just uses the buffer the regular reader returns:
with this Javascript:
I will keep using the second version. I have looked at the performance implications of using either and the second one just makes more sense: The Javascript runtime already knows in what chunks it is reading a file, and we might as well just operate on those. In contrast, the BYOB version potentially uses smaller chunks than what the Javascript runtime used and thus incurs a bunch of overhead with all the back-and-forth between .NET and JS (plus additional copying).
Using the second
|
As an aside, the example-code above suffers not just from the buffering but also from the fact that frequent |
Really great work @sschoener 😁 It makes sense. It's great to get a headstart on how to make some alternative implementations. I will look into these as well and find the best solution. I think your focus on some sort of benchmarking is also a very valid point. If we end up finding that there are trade-offs between correctness, convenience, and performance it might make sense to make it configurable which strategy it will use for stream reading. Your comment regarding the async invocations is also very valid. For many of my libraries, I have synchronous versions of each class if they have any methods of properties that can be invoked synchronously. Blazor.Streams is no exception and I actually have an InProcess variant of the |
I'm not actually sure how to get rid of the While this is definitely an additional issue, I'm taking the opportunity to also inform you that you can save a good bit of time on the writing side by special-casing for arrays (that's the common case) and hence skip the expensive copy in
where
|
If you want to read more about synchronous JSInterop then you might want to read this: IJSInProcessRuntime |
Neat, thank you! I didn't know about that. I've really only started using Blazor yesterday, so it's all pretty new :). I'm not sure how much the in-process runtime would help here since the |
There are definitely some improvements possible like reading the value of the |
Hello there! I have attempted to use your FileSystemAccess library in Blazor -- thank you for your work on all this. I have noticed that the stream returned by
File.StreamAsync()
doesn't work in most scenarios I have tried (the file is acquired by using your APIs to show an "Open Window dialog" etc.). For example, when you have a 90MB file and just need to read 1KB of data, you would usually useWhat this should do is read 1KB worth of data. What it actually does is throw an exception because the stream implementation doesn't respect the buffer length, as you can see from the implementation. Similarly,
stream.Read()
(reading a single byte) doesn't work.Here is the typical callstack:
The
Destiniation is too short
message likely stems from the call to(await helper.InvokeAsync<byte[]>("byteArray", jSValue)).CopyTo(buffer);
.For similar reasons, using something like
new StreamReader(stream).ReadLineAsync()
doesn't work, unless you set theStreamReader
buffer size to a value that sits above the size of the buffer that the browser has chosen. For example, I can get consistent failures when I set the buffer size of aStreamReader
to 1MB but it works with 16MB. For now, that is, until Chrome changes its implementation and use larger buffers in the future :)This is ultimately due to a difference between how JavaScript and C# choose to handle I/O: In C#, I get to choose the buffer size (the reader might still have its own buffer somewhere else for efficiency, but it'll handle whatever buffer size I give it). In Javascript, the browser gets to choose the buffer size and the
read
functions just spits out chunk in the sizes that the browser has chosen.One option for fixing this could be to add an internally buffer to the
ReadableStream
itself or the reader types.The text was updated successfully, but these errors were encountered: