Skip to content

Commit bb1d9c8

Browse files
committed
Added support for custom JS module path, issue #29
1 parent 1cdd267 commit bb1d9c8

18 files changed

Lines changed: 235 additions & 61 deletions

samples/KristofferStrube.Blazor.FileSystemAccess.ServerExample/Pages/IndexedDB.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ In this sample we store the references to files that we have previously opened i
4242
StoredFileHandles = (await Task.WhenAll(Enumerable
4343
.Range(0, length)
4444
.Select(async i => {
45-
var fileHandle = FileSystemFileHandle.Create(JSRuntime, await entries.InvokeAsync<IJSObjectReference>("at", i));
45+
var fileHandle = this.FileSystemAccessService.CreateFileHandle(JSRuntime, await entries.InvokeAsync<IJSObjectReference>("at", i));
4646
return (false, fileHandle, await fileHandle.GetNameAsync());
4747
})
4848
)).ToList();

samples/KristofferStrube.Blazor.FileSystemAccess.WasmExample/Pages/IndexedDB.razor

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ In this sample we store the references to files that we have previously opened i
4040
var length = await JSRuntime.InvokeAsync<int>("getAttribute", entries, "length");
4141
StoredFileHandles = (await Task.WhenAll(Enumerable
4242
.Range(0, length)
43-
.Select(async i => (false, await FileSystemFileHandleInProcess.CreateAsync(JSRuntime, await entries.InvokeAsync<IJSInProcessObjectReference>("at", i))))
43+
.Select(async i => (false,
44+
await this.FileSystemAccessService.CreateFileHandleInProcessAsync(
45+
JSRuntime,
46+
await entries.InvokeAsync<IJSInProcessObjectReference>("at", i))))
4447
)).ToList();
4548
}
4649

samples/KristofferStrube.Blazor.FileSystemAccess.WasmExample/Program.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using KristofferStrube.Blazor.FileAPI;
22
using KristofferStrube.Blazor.FileSystemAccess;
3+
using KristofferStrube.Blazor.FileSystemAccess.Options;
34
using KristofferStrube.Blazor.FileSystemAccess.WasmExample;
45
using Microsoft.AspNetCore.Components.Web;
56
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
@@ -10,7 +11,14 @@
1011
builder.RootComponents.Add<HeadOutlet>("head::after");
1112

1213
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
13-
builder.Services.AddFileSystemAccessServiceInProcess();
14+
15+
// Configure with custom script path
16+
builder.Services.AddFileSystemAccessServiceInProcess(options =>
17+
{
18+
// The file at this path in this example is manually copied to wwwroot folder
19+
options.ScriptPath = $"/content/custom-path/{FileSystemAccessOptions.DefaultNamespace}.js";
20+
});
21+
1422
builder.Services.AddURLServiceInProcess();
1523

1624
// Adding and configuring IndexedDB used for the IndexedDB sample.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export function size(array) { return array.length; }
2+
3+
export function getAttribute(object, attribute) { return object[attribute]; }
4+
5+
export async function arrayFrom(values) {
6+
var res = []
7+
for await (let value of values) {
8+
res.push(value);
9+
}
10+
return res;
11+
}
12+
13+
export function WriteBlobWriteParams(fileSystemWritableFileStream, writeParams, blob) {
14+
writeParams.data = blob;
15+
fileSystemWritableFileStream.write(writeParams);
16+
}
17+
18+
export async function arrayBuffer(blob) {
19+
var buffer = await blob.arrayBuffer();
20+
var bytes = new Uint8Array(buffer);
21+
return bytes;
22+
}

src/KristofferStrube.Blazor.FileSystemAccess/BaseJSWrapper.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using KristofferStrube.Blazor.FileSystemAccess.Extensions;
2+
using KristofferStrube.Blazor.FileSystemAccess.Options;
23
using Microsoft.JSInterop;
34

45
namespace KristofferStrube.Blazor.FileSystemAccess;
@@ -8,15 +9,17 @@ public abstract class BaseJSWrapper : IAsyncDisposable
89
public readonly IJSObjectReference JSReference;
910
protected readonly Lazy<Task<IJSObjectReference>> helperTask;
1011
protected readonly IJSRuntime jSRuntime;
12+
protected readonly FileSystemAccessOptions? options;
1113

1214
/// <summary>
1315
/// Constructs a wrapper instance for an equivalent JS instance.
1416
/// </summary>
1517
/// <param name="jSRuntime">An <see cref="IJSRuntime"/> instance.</param>
1618
/// <param name="jSReference">A JS reference to an existing JS instance that should be wrapped.</param>
17-
internal BaseJSWrapper(IJSRuntime jSRuntime, IJSObjectReference jSReference)
19+
internal BaseJSWrapper(IJSRuntime jSRuntime, IJSObjectReference jSReference, FileSystemAccessOptions? options)
1820
{
19-
helperTask = new(jSRuntime.GetHelperAsync);
21+
this.options = options;
22+
helperTask = new(async () => await jSRuntime.GetHelperAsync(options));
2023
JSReference = jSReference;
2124
this.jSRuntime = jSRuntime;
2225
}
Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
using Microsoft.JSInterop;
1+
using KristofferStrube.Blazor.FileSystemAccess.Options;
2+
using Microsoft.Extensions.Options;
3+
using Microsoft.JSInterop;
24

35
namespace KristofferStrube.Blazor.FileSystemAccess.Extensions;
46

57
internal static class IJSRuntimeExtensions
68
{
7-
internal static async Task<IJSObjectReference> GetHelperAsync(this IJSRuntime jSRuntime)
8-
{
9-
return await jSRuntime.InvokeAsync<IJSObjectReference>(
10-
"import", "./_content/KristofferStrube.Blazor.FileSystemAccess/KristofferStrube.Blazor.FileSystemAccess.js");
11-
}
9+
internal static async Task<IJSObjectReference> GetHelperAsync(this IJSRuntime jSRuntime, FileSystemAccessOptions? options)
10+
=> await GetHelperAsync<IJSObjectReference>(jSRuntime, options);
1211

13-
internal static async Task<IJSInProcessObjectReference> GetInProcessHelperAsync(this IJSRuntime jSRuntime)
14-
{
15-
return await jSRuntime.InvokeAsync<IJSInProcessObjectReference>(
16-
"import", "./_content/KristofferStrube.Blazor.FileSystemAccess/KristofferStrube.Blazor.FileSystemAccess.js");
17-
}
12+
internal static async Task<IJSInProcessObjectReference> GetInProcessHelperAsync(this IJSRuntime jSRuntime, FileSystemAccessOptions? options) =>
13+
await GetHelperAsync<IJSInProcessObjectReference>(jSRuntime, options);
14+
15+
static async Task<T> GetHelperAsync<T>(IJSRuntime jSRuntime, FileSystemAccessOptions? options) =>
16+
await jSRuntime.InvokeAsync<T>("import", GetScriptPath(options));
17+
18+
static string GetScriptPath(FileSystemAccessOptions? options) => options?.ScriptPath ?? FileSystemAccessOptions.DefaultPath;
1819
}
Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using Microsoft.Extensions.DependencyInjection;
1+
using KristofferStrube.Blazor.FileSystemAccess.Options;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Microsoft.Extensions.Options;
24
using Microsoft.JSInterop;
35

46
namespace KristofferStrube.Blazor.FileSystemAccess;
@@ -7,12 +9,37 @@ public static class IServiceCollectionExtensions
79
{
810
public static IServiceCollection AddFileSystemAccessService(this IServiceCollection serviceCollection)
911
{
12+
return AddFileSystemAccessService(serviceCollection, null);
13+
}
14+
15+
public static IServiceCollection AddFileSystemAccessService(this IServiceCollection serviceCollection, Action<FileSystemAccessOptions>? configure)
16+
{
17+
if (configure is not null)
18+
{
19+
serviceCollection.Configure(configure);
20+
}
21+
1022
return serviceCollection.AddScoped<IFileSystemAccessService, FileSystemAccessService>();
1123
}
24+
1225
public static IServiceCollection AddFileSystemAccessServiceInProcess(this IServiceCollection serviceCollection)
1326
{
27+
return AddFileSystemAccessServiceInProcess(serviceCollection, null);
28+
}
29+
30+
public static IServiceCollection AddFileSystemAccessServiceInProcess(this IServiceCollection serviceCollection, Action<FileSystemAccessOptions>? configure)
31+
{
32+
33+
if (configure is not null)
34+
{
35+
serviceCollection.Configure(configure);
36+
}
37+
1438
return serviceCollection
15-
.AddScoped<IFileSystemAccessServiceInProcess>(sp => new FileSystemAccessServiceInProcess((IJSInProcessRuntime)sp.GetRequiredService<IJSRuntime>()))
39+
.AddScoped<IFileSystemAccessServiceInProcess>(
40+
sp => new FileSystemAccessServiceInProcess(
41+
(IJSInProcessRuntime)sp.GetRequiredService<IJSRuntime>(),
42+
sp.GetRequiredService<IOptions<FileSystemAccessOptions>>()))
1643
.AddScoped<IFileSystemAccessService>(sp => sp.GetRequiredService<IFileSystemAccessServiceInProcess>());
1744
}
1845
}

src/KristofferStrube.Blazor.FileSystemAccess/FileSystemAccessService.InProcess.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using KristofferStrube.Blazor.FileSystemAccess.Options;
2+
using Microsoft.Extensions.Options;
13
using Microsoft.JSInterop;
24

35
namespace KristofferStrube.Blazor.FileSystemAccess;
@@ -6,7 +8,7 @@ public class FileSystemAccessServiceInProcess : FileSystemAccessService, IFileSy
68
{
79
protected new readonly IJSInProcessRuntime jSRuntime;
810

9-
public FileSystemAccessServiceInProcess(IJSInProcessRuntime jSRuntime) : base(jSRuntime)
11+
public FileSystemAccessServiceInProcess(IJSInProcessRuntime jSRuntime, IOptions<FileSystemAccessOptions> options) : base(jSRuntime, options)
1012
{
1113
this.jSRuntime = jSRuntime;
1214
}
@@ -49,7 +51,10 @@ private async Task<FileSystemFileHandleInProcess[]> ShowOpenFilePickerPrivateAsy
4951
Enumerable
5052
.Range(0, length)
5153
.Select(async i =>
52-
await FileSystemFileHandleInProcess.CreateAsync(jSRuntime, await jSFileHandles.InvokeAsync<IJSInProcessObjectReference>("at", i))
54+
await FileSystemFileHandleInProcess.CreateAsync(
55+
jSRuntime,
56+
await jSFileHandles.InvokeAsync<IJSInProcessObjectReference>("at", i),
57+
this.options)
5358
)
5459
.ToArray()
5560
);
@@ -87,7 +92,7 @@ await FileSystemFileHandleInProcess.CreateAsync(jSRuntime, await jSFileHandles.I
8792
private async Task<FileSystemFileHandleInProcess> ShowSaveFilePickerPrivateAsync(object? options)
8893
{
8994
IJSInProcessObjectReference jSFileHandle = await jSRuntime.InvokeAsync<IJSInProcessObjectReference>("window.showSaveFilePicker", options);
90-
return await FileSystemFileHandleInProcess.CreateAsync(jSRuntime, jSFileHandle);
95+
return await FileSystemFileHandleInProcess.CreateAsync(jSRuntime, jSFileHandle, this.options);
9196
}
9297

9398
/// <summary>
@@ -123,7 +128,7 @@ private async Task<FileSystemFileHandleInProcess> ShowSaveFilePickerPrivateAsync
123128
private async Task<FileSystemDirectoryHandleInProcess> ShowDirectoryPickerPrivateAsync(object? options)
124129
{
125130
IJSInProcessObjectReference jSFileHandle = await jSRuntime.InvokeAsync<IJSInProcessObjectReference>("window.showDirectoryPicker", options);
126-
return await FileSystemDirectoryHandleInProcess.CreateAsync(jSRuntime, jSFileHandle);
131+
return await FileSystemDirectoryHandleInProcess.CreateAsync(jSRuntime, jSFileHandle, this.options);
127132
}
128133

129134
/// <summary>
@@ -133,6 +138,6 @@ private async Task<FileSystemDirectoryHandleInProcess> ShowDirectoryPickerPrivat
133138
public new async Task<FileSystemDirectoryHandleInProcess> GetOriginPrivateDirectoryAsync()
134139
{
135140
IJSInProcessObjectReference jSFileHandle = await jSRuntime.InvokeAsync<IJSInProcessObjectReference>("navigator.storage.getDirectory");
136-
return await FileSystemDirectoryHandleInProcess.CreateAsync(jSRuntime, jSFileHandle);
141+
return await FileSystemDirectoryHandleInProcess.CreateAsync(jSRuntime, jSFileHandle, this.options);
137142
}
138143
}

src/KristofferStrube.Blazor.FileSystemAccess/FileSystemAccessService.cs

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using KristofferStrube.Blazor.FileSystemAccess.Extensions;
2+
using KristofferStrube.Blazor.FileSystemAccess.Options;
3+
using Microsoft.Extensions.Options;
24
using Microsoft.JSInterop;
35

46
namespace KristofferStrube.Blazor.FileSystemAccess;
@@ -7,10 +9,13 @@ public class FileSystemAccessService : IAsyncDisposable, IFileSystemAccessServic
79
{
810
protected readonly Lazy<Task<IJSObjectReference>> helperTask;
911
protected readonly IJSRuntime jSRuntime;
12+
protected readonly FileSystemAccessOptions options;
1013

11-
public FileSystemAccessService(IJSRuntime jSRuntime)
14+
public FileSystemAccessService(IJSRuntime jSRuntime, IOptions<FileSystemAccessOptions> options)
1215
{
13-
helperTask = new(() => jSRuntime.GetHelperAsync());
16+
this.options = options.Value;
17+
18+
helperTask = new(() => jSRuntime.GetHelperAsync(this.options));
1419
this.jSRuntime = jSRuntime;
1520
}
1621

@@ -52,7 +57,10 @@ private async Task<FileSystemFileHandle[]> ShowOpenFilePickerPrivateAsync(object
5257
Enumerable
5358
.Range(0, length)
5459
.Select(async i =>
55-
new FileSystemFileHandle(jSRuntime, await jSFileHandles.InvokeAsync<IJSObjectReference>("at", i))
60+
new FileSystemFileHandle(
61+
jSRuntime,
62+
await jSFileHandles.InvokeAsync<IJSObjectReference>("at", i),
63+
this.options)
5664
)
5765
.ToArray()
5866
);
@@ -90,7 +98,7 @@ public async Task<FileSystemFileHandle> ShowSaveFilePickerAsync()
9098
private async Task<FileSystemFileHandle> ShowSaveFilePickerPrivateAsync(object? options)
9199
{
92100
IJSObjectReference jSFileHandle = await jSRuntime.InvokeAsync<IJSObjectReference>("window.showSaveFilePicker", options);
93-
return new FileSystemFileHandle(jSRuntime, jSFileHandle);
101+
return new FileSystemFileHandle(jSRuntime, jSFileHandle, this.options);
94102
}
95103

96104
/// <summary>
@@ -126,7 +134,7 @@ public async Task<FileSystemDirectoryHandle> ShowDirectoryPickerAsync()
126134
private async Task<FileSystemDirectoryHandle> ShowDirectoryPickerPrivateAsync(object? options)
127135
{
128136
IJSObjectReference jSFileHandle = await jSRuntime.InvokeAsync<IJSObjectReference>("window.showDirectoryPicker", options);
129-
return new FileSystemDirectoryHandle(jSRuntime, jSFileHandle);
137+
return new FileSystemDirectoryHandle(jSRuntime, jSFileHandle, this.options);
130138
}
131139

132140
/// <summary>
@@ -136,7 +144,7 @@ private async Task<FileSystemDirectoryHandle> ShowDirectoryPickerPrivateAsync(ob
136144
public async Task<FileSystemDirectoryHandle> GetOriginPrivateDirectoryAsync()
137145
{
138146
IJSObjectReference jSFileHandle = await jSRuntime.InvokeAsync<IJSObjectReference>("navigator.storage.getDirectory");
139-
return new FileSystemDirectoryHandle(jSRuntime, jSFileHandle);
147+
return new FileSystemDirectoryHandle(jSRuntime, jSFileHandle, this.options);
140148
}
141149

142150
/// <summary>
@@ -151,6 +159,46 @@ await jSRuntime.InvokeAsync<bool>("window.hasOwnProperty", "showSaveFilePicker")
151159
await jSRuntime.InvokeAsync<bool>("window.hasOwnProperty", "showDirectoryPicker");
152160
}
153161

162+
#region Create Handle Instances
163+
164+
/// <summary>
165+
/// Create a FileSystemHandle wrapper for JS handle.
166+
/// </summary>
167+
public FileSystemHandle CreateFileSystemHandle(IJSRuntime jSRuntime, IJSObjectReference jSReference)
168+
=> FileSystemHandle.Create(jSRuntime, jSReference, this.options);
169+
170+
/// <summary>
171+
/// Create a FileSystemFileHandle wrapper for JS handle.
172+
/// </summary>
173+
public FileSystemFileHandle CreateFileHandle(IJSRuntime jSRuntime, IJSObjectReference jSReference)
174+
=> FileSystemFileHandle.Create(jSRuntime, jSReference, this.options);
175+
176+
/// <summary>
177+
/// Create a FileSystemFileHandle wrapper for JS handle.
178+
/// </summary>
179+
public async Task<FileSystemHandleInProcess> CreateFileSystemHandleInProcessAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference)
180+
=> await FileSystemHandleInProcess.CreateAsync(jSRuntime, jSReference, this.options);
181+
182+
/// <summary>
183+
/// Create a FileSystemFileHandleInProcess wrapper for JS handle.
184+
/// </summary>
185+
public async Task<FileSystemFileHandleInProcess> CreateFileHandleInProcessAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference)
186+
=> await FileSystemFileHandleInProcess.CreateAsync(jSRuntime, jSReference, this.options);
187+
188+
/// <summary>
189+
/// Create a FileSystemDirectoryHandle wrapper for JS handle.
190+
/// </summary>
191+
public FileSystemDirectoryHandle CreateDirectoryHandle(IJSRuntime jSRuntime, IJSObjectReference jSReference)
192+
=> FileSystemDirectoryHandle.Create(jSRuntime, jSReference, this.options);
193+
194+
/// <summary>
195+
/// Create a FileSystemDirectoryHandleInProcess wrapper for JS handle.
196+
/// </summary>
197+
public async Task<FileSystemDirectoryHandleInProcess> CreateDirectoryHandleInProcessAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference)
198+
=> await FileSystemDirectoryHandleInProcess.CreateAsync(jSRuntime, jSReference, this.options);
199+
200+
#endregion
201+
154202
public async ValueTask DisposeAsync()
155203
{
156204
if (helperTask.IsValueCreated)

src/KristofferStrube.Blazor.FileSystemAccess/FileSystemDirectoryHandle.InProcess.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using KristofferStrube.Blazor.FileSystemAccess.Extensions;
2+
using KristofferStrube.Blazor.FileSystemAccess.Options;
23
using Microsoft.JSInterop;
34

45
namespace KristofferStrube.Blazor.FileSystemAccess;
@@ -11,13 +12,17 @@ public class FileSystemDirectoryHandleInProcess : FileSystemDirectoryHandle
1112
public new IJSInProcessObjectReference JSReference;
1213
protected readonly IJSInProcessObjectReference inProcessHelper;
1314

15+
[Obsolete("Use CreateDirectoryHandleInProcessAsync from IFileSystemAccessService instead")]
1416
public static async Task<FileSystemDirectoryHandleInProcess> CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference)
17+
=> await CreateAsync(jSRuntime, jSReference, null);
18+
19+
internal static async Task<FileSystemDirectoryHandleInProcess> CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference, FileSystemAccessOptions? options)
1520
{
16-
IJSInProcessObjectReference inProcessHelper = await jSRuntime.GetInProcessHelperAsync();
17-
return new FileSystemDirectoryHandleInProcess(jSRuntime, inProcessHelper, jSReference);
21+
IJSInProcessObjectReference inProcessHelper = await jSRuntime.GetInProcessHelperAsync(options);
22+
return new FileSystemDirectoryHandleInProcess(jSRuntime, inProcessHelper, jSReference, options);
1823
}
1924

20-
internal FileSystemDirectoryHandleInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference) : base(jSRuntime, jSReference)
25+
internal FileSystemDirectoryHandleInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference, FileSystemAccessOptions? options) : base(jSRuntime, jSReference, options)
2126
{
2227
this.inProcessHelper = inProcessHelper;
2328
JSReference = jSReference;
@@ -37,7 +42,10 @@ internal FileSystemDirectoryHandleInProcess(IJSRuntime jSRuntime, IJSInProcessOb
3742
Enumerable
3843
.Range(0, length)
3944
.Select(async i =>
40-
await FileSystemHandleInProcess.CreateAsync(jSRuntime, await jSEntries.InvokeAsync<IJSInProcessObjectReference>("at", i))
45+
await FileSystemHandleInProcess.CreateAsync(
46+
jSRuntime,
47+
await jSEntries.InvokeAsync<IJSInProcessObjectReference>("at", i),
48+
this.options)
4149
)
4250
.ToArray()
4351
);
@@ -46,12 +54,12 @@ await FileSystemHandleInProcess.CreateAsync(jSRuntime, await jSEntries.InvokeAsy
4654
public new async Task<FileSystemFileHandleInProcess> GetFileHandleAsync(string name, FileSystemGetFileOptions? options = null)
4755
{
4856
IJSInProcessObjectReference jSFileSystemFileHandle = await JSReference.InvokeAsync<IJSInProcessObjectReference>("getFileHandle", name, options);
49-
return new FileSystemFileHandleInProcess(jSRuntime, inProcessHelper, jSFileSystemFileHandle);
57+
return new FileSystemFileHandleInProcess(jSRuntime, inProcessHelper, jSFileSystemFileHandle, this.options);
5058
}
5159

5260
public new async Task<FileSystemDirectoryHandleInProcess> GetDirectoryHandleAsync(string name, FileSystemGetDirectoryOptions? options = null)
5361
{
5462
IJSInProcessObjectReference jSFileSystemDirectoryHandle = await JSReference.InvokeAsync<IJSInProcessObjectReference>("getDirectoryHandle", name, options);
55-
return new FileSystemDirectoryHandleInProcess(jSRuntime, inProcessHelper, jSFileSystemDirectoryHandle);
63+
return new FileSystemDirectoryHandleInProcess(jSRuntime, inProcessHelper, jSFileSystemDirectoryHandle, this.options);
5664
}
5765
}

0 commit comments

Comments
 (0)