diff --git a/src/Uno.UWP/Storage/StreamedFileDataRequest.cs b/src/Uno.UWP/Storage/StreamedFileDataRequest.cs index 343d935266b1..b49f04cb3cfb 100644 --- a/src/Uno.UWP/Storage/StreamedFileDataRequest.cs +++ b/src/Uno.UWP/Storage/StreamedFileDataRequest.cs @@ -3,6 +3,7 @@ using System; using System.IO; using System.Threading; +using System.Threading.Tasks; using Windows.ApplicationModel.Appointments; using Windows.Foundation; using Windows.Storage.Streams; @@ -11,14 +12,14 @@ namespace Windows.Storage { public sealed partial class StreamedFileDataRequest : IOutputStream, IDisposable, IStreamedFileDataRequest { - private readonly StreamedRandomAccessStream _owner; + private readonly StreamedCustomDataLoader _owner; private readonly Stream _tempFile; private readonly CancellationTokenSource _ct; - internal StreamedFileDataRequest(StreamedRandomAccessStream owner, Stream tempFile) + internal StreamedFileDataRequest(StreamedCustomDataLoader owner) { _owner = owner; - _tempFile = tempFile; + _tempFile = owner.File.OpenWeak(FileAccess.Write); _ct = new CancellationTokenSource(); } @@ -29,16 +30,19 @@ internal StreamedFileDataRequest(StreamedRandomAccessStream owner, Stream tempFi public IAsyncOperationWithProgress WriteAsync(IBuffer buffer) => new AsyncOperationWithProgress(async (ct, op) => { - var write = _tempFile.WriteAsyncOperation(buffer); - write.Progress = (snd, p) => op.NotifyProgress(p); + try + { + await _tempFile.WriteAsync(buffer, ct); + await _tempFile.FlushAsync(ct); // We make sure to write the data to the disk before allow read to access it + } + catch (IOException e) + { + throw new OperationCanceledException("The download of this file has been cancelled", e); + } - var written = await write.AsTask(ct); + _owner.ReportDataWritten(buffer.Capacity); - // We make sure to write the data to the disk before allow read to access it - _tempFile.FlushAsync(ct); - //_owner.OnDataLoadProgress(written); - - return written; + return buffer.Capacity; }); public IAsyncOperation FlushAsync() @@ -46,13 +50,19 @@ public IAsyncOperation FlushAsync() public void FailAndClose(StreamedFileFailureMode failureMode) { - //_owner.OnDataLoadCompleted(failureMode); + _ct.Dispose(); + _tempFile.Dispose(); + _owner.ReportLoadCompleted(failureMode); } public void Dispose() { _ct.Dispose(); - //_owner.OnDataLoadCompleted(default); + _tempFile.Dispose(); + _owner.ReportLoadCompleted(); } + + ~StreamedFileDataRequest() + => Dispose(); } } diff --git a/src/Uno.UWP/Storage/StreamedFileDataRequestedHandler.cs b/src/Uno.UWP/Storage/StreamedFileDataRequestedHandler.cs index 11084d2d2556..4b905a2aa757 100644 --- a/src/Uno.UWP/Storage/StreamedFileDataRequestedHandler.cs +++ b/src/Uno.UWP/Storage/StreamedFileDataRequestedHandler.cs @@ -1,80 +1,8 @@ #nullable enable using System; -using System.IO; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Uno; -using Uno.Extensions; namespace Windows.Storage { public delegate void StreamedFileDataRequestedHandler(StreamedFileDataRequest stream); - - //internal static class StreamedFileDataRequestedHandlerHelper - //{ - // public static StreamedFileDataRequestedHandler CreateFromUri( - // Uri uri, - // HttpMethod? method = null, - // HttpClient? client = null, - // ActionAsync? onReady = null) - // { - // method ??= HttpMethod.Get; - // client ??= new HttpClient(); - - // return req => Task.Run(() => FetchAsync(req, uri, method, client, onReady, req.CancellationToken)); - // } - - // private static async Task FetchAsync( - // StreamedFileDataRequest req, - // Uri uri, - // HttpMethod method, - // HttpClient client, - // ActionAsync? onReady, - // CancellationToken ct) - // { - // try - // { - // HttpResponseMessage response; - // try - // { - // var request = new HttpRequestMessage(method, uri); - // response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct); - - // response.EnsureSuccessStatusCode(); - - // if (onReady is {}) - // { - // await onReady(ct, response, null); - // } - // } - // catch (Exception e) when (onReady is {}) - // { - // await onReady(ct, null, e); - // throw; - // } - - // if (response.Content is { } content) - // { - // var responseStream = await content.ReadAsStreamAsync(); - // await responseStream.CopyToAsync(req.AsStreamForWrite(), global::Windows.Storage.Streams.Buffer.DefaultCapacity, ct); - // } - // } - // catch (Exception e) - // { - // if (req.Log().IsEnabled(LogLevel.Warning)) - // { - // req.Log().LogWarning("Failed to load content", e); - // } - - // req.FailAndClose(StreamedFileFailureMode.Failed); - // } - // finally - // { - // req.Dispose(); - // } - // } - //} } diff --git a/src/Uno.UWP/Storage/Streams/StreamedCustomDataLoader.cs b/src/Uno.UWP/Storage/Streams/StreamedCustomDataLoader.cs new file mode 100644 index 000000000000..bcec6f65e6d5 --- /dev/null +++ b/src/Uno.UWP/Storage/Streams/StreamedCustomDataLoader.cs @@ -0,0 +1,64 @@ +#nullable enable + +using System; +using System.Linq; +using Windows.Foundation; + +namespace Windows.Storage.Streams +{ + internal class StreamedCustomDataLoader : IStreamedDataLoader + { + private uint _loaded; + private bool _isCompleted; + private StreamedFileFailureMode? _failure; + + public StreamedCustomDataLoader(StreamedFileDataRequestedHandler handler, TemporaryFile? tempFile = null) + { + File = tempFile ?? new TemporaryFile(); + + handler(new StreamedFileDataRequest(this)); + } + + /// + public event TypedEventHandler? DataUpdated; + + /// + public TemporaryFile File { get; } + + /// + public string? ContentType { get; } = RandomAccessStreamWithContentType.DefaultContentType; + + /// + public void CheckState() + { + if (_failure.HasValue) + { + throw new InvalidOperationException($"The async load of the data has failed ('{_failure.Value}')"); + } + } + + /// + public bool CanRead(ulong position) + => _isCompleted || position < _loaded; + + internal void ReportDataWritten(uint length) + { + _loaded += length; + DataUpdated?.Invoke(this, default); + } + + internal void ReportLoadCompleted(StreamedFileFailureMode? failure = null) + { + // Note: this is expected to be invoke more than once! + + _failure ??= failure; + + if (!_isCompleted) + { + _isCompleted = true; + + DataUpdated?.Invoke(this, default); + } + } + } +}