-
Notifications
You must be signed in to change notification settings - Fork 725
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(storage): [Wasm] Add support for GetFileFromApplicationUriAsync
- Loading branch information
1 parent
611dc70
commit d3bc03a
Showing
10 changed files
with
257 additions
and
8 deletions.
There are no files selected for viewing
1 change: 1 addition & 0 deletions
1
src/SamplesApp/UITests.Shared/Asset_GetFileFromApplicationUriAsync.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<SomeContent/> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
src/Uno.UI.RuntimeTests/Tests/Windows_Storage/Given_ApplicationStorage.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using System; | ||
using System.IO; | ||
using System.Threading.Tasks; | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using Windows.Storage; | ||
using Windows.UI.Xaml.Input; | ||
|
||
namespace Uno.UI.RuntimeTests.Tests.Windows_Storage | ||
{ | ||
[TestClass] | ||
public class Given_ApplicationStorage | ||
{ | ||
[TestMethod] | ||
public async Task When_GetFileFromApplicationUriAsync_RootPath() | ||
{ | ||
var file = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Asset_GetFileFromApplicationUriAsync.xml")); | ||
|
||
var content = await FileIO.ReadTextAsync(file); | ||
|
||
Assert.AreEqual("<SomeContent/>", content); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
| ||
// eslint-disable-next-line @typescript-eslint/no-namespace | ||
namespace Windows.Storage { | ||
|
||
export class AssetManager { | ||
public static async DownloadAssetsManifest(path: string): Promise<string> { | ||
var response = await fetch(path); | ||
return response.text(); | ||
} | ||
|
||
public static async DownloadAsset(path: string): Promise<string> { | ||
const response = await fetch(path); | ||
const arrayBuffer = await response.blob().then(b => <ArrayBuffer>(<any>b).arrayBuffer()); | ||
const size = arrayBuffer.byteLength; | ||
const responseArray = new Uint8ClampedArray(arrayBuffer); | ||
|
||
const pData = Module._malloc(size); | ||
|
||
Module.HEAPU8.set(responseArray, pData); | ||
|
||
return `${pData};${size}`; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
#nullable enable | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Globalization; | ||
using System.IO; | ||
using System.Runtime.InteropServices; | ||
using System.Text.RegularExpressions; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Uno.Disposables; | ||
using Uno.Extensions; | ||
using Uno.Foundation; | ||
using Uno.Threading; | ||
using Windows.Devices.AllJoyn; | ||
using Windows.Foundation; | ||
using Windows.Media.Streaming.Adaptive; | ||
using Windows.Security.Cryptography.Core; | ||
using Windows.Storage.FileProperties; | ||
using Windows.Storage.Streams; | ||
using Windows.UI.WebUI; | ||
|
||
namespace Windows.Storage | ||
{ | ||
internal partial class AssetsManager | ||
{ | ||
private static readonly string UNO_BOOTSTRAP_APP_BASE = Environment.GetEnvironmentVariable(nameof(UNO_BOOTSTRAP_APP_BASE)); | ||
|
||
private static readonly Lazy<Task<HashSet<string>>> _assets = new Lazy<Task<HashSet<string>>>(() => GetAssets(CancellationToken.None)); | ||
private static readonly Dictionary<string, DownloadEntry> _assetsGate = new Dictionary<string, DownloadEntry>(); | ||
|
||
private static async Task<HashSet<string>> GetAssets(CancellationToken ct) | ||
{ | ||
var assetsUri = !string.IsNullOrEmpty(UNO_BOOTSTRAP_APP_BASE) ? $"{UNO_BOOTSTRAP_APP_BASE}/uno-assets.txt" : "uno-assets.txt"; | ||
|
||
var assets = await WebAssemblyRuntime.InvokeAsync($"Windows.Storage.AssetManager.DownloadAssetsManifest(\'{assetsUri}\')"); | ||
|
||
return new HashSet<string>(Regex.Split(assets, "\r\n|\r|\n"), StringComparer.OrdinalIgnoreCase); | ||
} | ||
|
||
public static async Task<string> DownloadAsset(CancellationToken ct, string assetPath) | ||
{ | ||
var updatedPath = assetPath.TrimStart("/"); | ||
var assetSet = await _assets.Value; | ||
|
||
if (assetSet.Contains(updatedPath)) | ||
{ | ||
var localPath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, ".assetsCache", UNO_BOOTSTRAP_APP_BASE, updatedPath); | ||
|
||
using var assetLock = await LockForAsset(ct, updatedPath); | ||
|
||
if (!File.Exists(localPath)) | ||
{ | ||
var assetUri = !string.IsNullOrEmpty(UNO_BOOTSTRAP_APP_BASE) ? $"{UNO_BOOTSTRAP_APP_BASE}/{updatedPath}" : updatedPath; | ||
var assetInfo = await WebAssemblyRuntime.InvokeAsync($"Windows.Storage.AssetManager.DownloadAsset(\'{assetUri}\')"); | ||
|
||
var parts = assetInfo.Split(';'); | ||
if (parts.Length == 2) | ||
{ | ||
var ptr = (IntPtr)int.Parse(parts[0], CultureInfo.InvariantCulture); | ||
var length = int.Parse(parts[1], CultureInfo.InvariantCulture); | ||
|
||
try | ||
{ | ||
var buffer = new byte[length]; | ||
Marshal.Copy(ptr, buffer, 0, length); | ||
|
||
Directory.CreateDirectory(Path.GetDirectoryName(localPath)); | ||
File.WriteAllBytes(localPath, buffer); | ||
} | ||
finally | ||
{ | ||
Marshal.FreeHGlobal(ptr); | ||
} | ||
} | ||
else | ||
{ | ||
throw new InvalidOperationException($"Invalid Windows.Storage.AssetManager.DownloadAsset return value"); | ||
} | ||
} | ||
|
||
return localPath; | ||
} | ||
else | ||
{ | ||
throw new FileNotFoundException($"The file [{assetPath}] cannot be found"); | ||
} | ||
} | ||
|
||
private static async Task<IDisposable> LockForAsset(CancellationToken ct, string updatedPath) | ||
{ | ||
DownloadEntry GetEntry() | ||
{ | ||
lock (_assetsGate) | ||
{ | ||
if (!_assetsGate.TryGetValue(updatedPath, out var entry)) | ||
{ | ||
_assetsGate[updatedPath] = entry = new DownloadEntry(); | ||
} | ||
|
||
entry.ReferenceCount++; | ||
|
||
return entry; | ||
} | ||
} | ||
|
||
var entry = GetEntry(); | ||
|
||
var disposable = await entry.Gate.LockAsync(ct); | ||
|
||
void ReleaseEntry() | ||
{ | ||
lock (_assetsGate) | ||
{ | ||
disposable.Dispose(); | ||
|
||
if(--entry.ReferenceCount == 0) | ||
{ | ||
_assetsGate.Remove(updatedPath); | ||
} | ||
} | ||
} | ||
|
||
return Disposable.Create(ReleaseEntry); | ||
} | ||
|
||
private class DownloadEntry | ||
{ | ||
public int ReferenceCount; | ||
public AsyncLock Gate { get; } = new AsyncLock(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously | ||
|
||
using System; | ||
using System.Globalization; | ||
using System.IO; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Uno.Extensions; | ||
using Windows.Foundation; | ||
using Windows.Storage.FileProperties; | ||
using Windows.Storage.Streams; | ||
|
||
namespace Windows.Storage | ||
{ | ||
public partial class StorageFile : StorageItem, IStorageFile | ||
{ | ||
private static async Task<StorageFile> GetFileFromApplicationUriAsyncTask(CancellationToken ct, Uri uri) | ||
{ | ||
if(uri.Scheme != "ms-appx") | ||
{ | ||
throw new InvalidOperationException("Uri is not using the ms-appx scheme"); | ||
} | ||
|
||
var path = uri.PathAndQuery; | ||
|
||
return await StorageFile.GetFileFromPathAsync(await AssetsManager.DownloadAsset(ct, path)); | ||
} | ||
} | ||
} |