Support seeking of BrowserFileStream
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 290
Description
### Is your feature request related to a problem? Please describe.
Some libraries that I want to use with Blazor uses `Stream.Seek` which isn't currently supported by Blazor, which mostly only seeks forward and not backward.
### Describe the solution you'd like
Forward only seeking can be accomplished by saving the `Seek` parameters and read it to a temporary buffer to simulate seeking so we wont need an async `Seek` function. Here is my implementation to illustrate it better:
```cs
public class SeekableBrowserFileStream : Stream
{
const int SEEK_BUFFER_SIZE = 20 * 1024 * 1024; // The larger the better the performance. Maybe auto detect browser's max buffer size.
Stream m_browserStream;
long m_seekForwardCount;
public override bool CanRead => m_browserStream.CanRead;
public override bool CanSeek => true; // Only forward.
public override bool CanWrite => m_browserStream.CanWrite;
public override long Length => m_browserStream.Length;
public override long Position
{
get => m_browserStream.Position + m_seekForwardCount;
set => throw new NotSupportedException(); // TODO: Allow set to forward position.
}
public BlazorFileStream(Stream browserStream)
{
m_browserStream = browserStream;
}
public override void Flush()
{
throw new NotSupportedException();
}
public override Task FlushAsync(CancellationToken cancellationToken)
{
return m_browserStream.FlushAsync(cancellationToken);
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (m_seekForwardCount > 0)
{
byte[] seekBuff = new byte[SEEK_BUFFER_SIZE];
long seekCount = m_seekForwardCount / SEEK_BUFFER_SIZE;
for (int i = 0; i < seekCount; ++i)
{
int read = await m_browserStream.ReadAsync(seekBuff, 0, SEEK_BUFFER_SIZE, cancellationToken);
m_seekForwardCount -= read;
}
while (m_seekForwardCount > 0)
{
int read = await m_browserStream.ReadAsync(seekBuff, 0, (int)m_seekForwardCount, cancellationToken);
m_seekForwardCount -= read;
}
}
return await m_browserStream.ReadAsync(buffer, offset, count, cancellationToken);
}
public override long Seek(long offset, SeekOrigin origin)
{
long posAfterSeek = Position + m_seekForwardCount;
if (origin == SeekOrigin.Begin)
{
posAfterSeek = offset;
}
else if (origin == SeekOrigin.Current)
{
posAfterSeek += offset;
}
else
{
posAfterSeek = (Length - 1) - offset;
}
if (posAfterSeek < Position)
{
throw new NotSupportedException("Cannot seek earlier than current position");
}
m_seekForwardCount = posAfterSeek - Position;
return posAfterSeek;
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}
```
Contributor guide
Assessment
This issue has not been assessed yet.