Open
Description
Is there an existing issue for this?
- I have searched the existing issues
Is your feature request related to a problem? Please describe the problem.
the PullFromJSDataStream
can be marked as seekable because the underlying js interop uses blob slicing.
Describe the solution you'd like
the class below is just a copy-paste from the existing PullFromJSDataStream
code. i tested this with a File
from input
of type file
on blazor wasm.
class WebStream : Stream
{
private readonly IJSRuntime _runtime;
private readonly IJSStreamReference _jsStreamReference;
private readonly long _totalLength;
private readonly CancellationToken _streamCancellationToken;
private long _offset;
public static WebStream CreateWebStream(
IJSRuntime runtime,
IJSStreamReference jsStreamReference,
long totalLength,
CancellationToken cancellationToken = default)
{
return new WebStream(
runtime,
jsStreamReference,
totalLength,
cancellationToken);
}
private WebStream(
IJSRuntime runtime,
IJSStreamReference jsStreamReference,
long totalLength,
CancellationToken cancellationToken)
{
_runtime = runtime;
_jsStreamReference = jsStreamReference;
_totalLength = totalLength;
_streamCancellationToken = cancellationToken;
_offset = 0;
}
public override bool CanRead => true;
public override bool CanSeek => true;
public override bool CanWrite => false;
public override long Length => _totalLength;
public override long Position
{
get => _offset;
set => Seek(value, SeekOrigin.Begin);
}
public override void Flush()
{
// No-op
}
public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public override int Read(byte[] buffer, int offset, int count)
=> throw new NotSupportedException("Synchronous reads are not supported.");
public override long Seek(long offset, SeekOrigin origin)
{
var newOffset = origin switch
{
SeekOrigin.Begin => offset,
SeekOrigin.Current => _offset + offset,
SeekOrigin.End => _totalLength + offset,
_ => throw new ArgumentOutOfRangeException(nameof(origin), origin, null),
};
if (newOffset < 0 || newOffset > _totalLength)
{
throw new ArgumentOutOfRangeException(nameof(offset), "Seek offset is out of bounds.");
}
_offset = newOffset;
return _offset;
}
public override void SetLength(long value)
=> throw new NotSupportedException();
public override void Write(byte[] buffer, int offset, int count)
=> throw new NotSupportedException();
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
=> await ReadAsync(buffer.AsMemory(offset, count), cancellationToken);
public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
var bytesRead = await RequestDataFromJSAsync(buffer.Length);
ThrowIfCancellationRequested(cancellationToken);
bytesRead.CopyTo(buffer);
return bytesRead.Length;
}
private void ThrowIfCancellationRequested(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested ||
_streamCancellationToken.IsCancellationRequested)
{
throw new TaskCanceledException();
}
}
private async ValueTask<byte[]> RequestDataFromJSAsync(int numBytesToRead)
{
numBytesToRead = (int)Math.Min(
numBytesToRead,
_totalLength - _offset);
var bytesRead = await _runtime.InvokeAsync<byte[]>(
"Blazor._internal.getJSDataStreamChunk",
_jsStreamReference,
_offset,
numBytesToRead);
if (bytesRead.Length != numBytesToRead)
{
throw new EndOfStreamException("Failed to read the requested number of bytes from the stream.");
}
_offset += bytesRead.Length;
if (_offset == _totalLength)
{
Dispose(true);
}
return bytesRead;
}
}
Additional context
No response