dotnet / dotnet/aspnetcore

Support seeking of BrowserFileStream

Open
#38,785 12 comments 7 reactions 0 assignees View on GitHub
area-blazor design-proposal enhancement feature-blazor-jsinterop Pillar: Complete Blazor Web
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.