csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
3.2 KiB
124 lines
3.2 KiB
using System;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices.JavaScript;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Avalonia.Browser.Interop;
|
|
|
|
namespace Avalonia.Browser.Storage;
|
|
|
|
// Loose wrapper implementaion of a stream on top of FileAPI FileSystemWritableFileStream
|
|
internal sealed class WriteableStream : Stream
|
|
{
|
|
private JSObject? _jSReference;
|
|
|
|
// Unfortunatelly we can't read current length/position, so we need to keep it C#-side only.
|
|
private long _length, _position;
|
|
|
|
internal WriteableStream(JSObject jSReference, long initialLength)
|
|
{
|
|
_jSReference = jSReference;
|
|
_length = initialLength;
|
|
}
|
|
|
|
private JSObject JSReference => _jSReference ?? throw new ObjectDisposedException(nameof(WriteableStream));
|
|
|
|
public override bool CanRead => false;
|
|
|
|
public override bool CanSeek => true;
|
|
|
|
public override bool CanWrite => true;
|
|
|
|
public override long Length => _length;
|
|
|
|
public override long Position
|
|
{
|
|
get => _position;
|
|
set => Seek(_position, SeekOrigin.Begin);
|
|
}
|
|
|
|
public override void Flush()
|
|
{
|
|
// no-op
|
|
}
|
|
|
|
public override int Read(byte[] buffer, int offset, int count)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
public override long Seek(long offset, SeekOrigin origin)
|
|
{
|
|
var position = origin switch
|
|
{
|
|
SeekOrigin.Current => _position + offset,
|
|
SeekOrigin.End => _length + offset,
|
|
_ => offset
|
|
};
|
|
StreamHelper.Seek(JSReference, position);
|
|
return position;
|
|
}
|
|
|
|
public override void SetLength(long value)
|
|
{
|
|
_length = value;
|
|
|
|
// See https://docs.w3cub.com/dom/filesystemwritablefilestream/truncate
|
|
// If the offset is smaller than the size, it remains unchanged. If the offset is larger than size, the offset is set to that size
|
|
if (_position > _length)
|
|
{
|
|
_position = _length;
|
|
}
|
|
|
|
StreamHelper.Truncate(JSReference, value);
|
|
}
|
|
|
|
public override void Write(byte[] buffer, int offset, int count)
|
|
{
|
|
throw new InvalidOperationException("Browser supports only WriteAsync");
|
|
}
|
|
|
|
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
|
|
{
|
|
return new ValueTask(WriteAsyncInternal(buffer.ToArray(), cancellationToken));
|
|
}
|
|
|
|
private Task WriteAsyncInternal(byte[] buffer, CancellationToken _)
|
|
{
|
|
_position += buffer.Length;
|
|
|
|
return StreamHelper.WriteAsync(JSReference, buffer);
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (_jSReference is { } jsReference)
|
|
{
|
|
_jSReference = null;
|
|
try
|
|
{
|
|
_ = StreamHelper.CloseAsync(jsReference);
|
|
}
|
|
finally
|
|
{
|
|
jsReference.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
public override async ValueTask DisposeAsync()
|
|
{
|
|
if (_jSReference is { } jsReference)
|
|
{
|
|
_jSReference = null;
|
|
try
|
|
{
|
|
await StreamHelper.CloseAsync(jsReference);
|
|
}
|
|
finally
|
|
{
|
|
jsReference.Dispose();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|