首页 Blazor WebAssembly 访问文件系统
文章
取消

Blazor WebAssembly 访问文件系统

如果你想与用户的文件系统互动,那么你需要使用HTML标准来访问文件系统,常见的访问本地文件的几种方式:读取文件并展示文件内容,修文件并保存至本地以及读取文件目录,利用浏览器的本地文件API并用JSInterop将其封装起来然后从Blazor向其传递文件流达到访问用户文件系统的目的。

读取图片并且展示

创建blazor webassembly项目 dotnet new blazorwasm –name BlazorFileAccess 在 index.html文件 <body>标记中增加js方法用来操作打开的文件,同时在根目录wwwroot 中增加js文件 fileAccess用户读取打开文件的参数。

1
2
3
4
5
6
7
8
<body>
...
<script>
window.jSReference = (element) => { return element.valueOf(); }
window.getAttribute = (object, attribute) => { return object[attribute]; }
window.setAttribute = (object, attribute, value) => { object[attribute] = value; }
</script>
</body>

fileAccess 文件内容

1
2
3
4
5
6
7
8
9
export function size(array) { return array.length; }
export function getAttribute(object, attribute) { return object[attribute]; }
export async function arrayFrom(values) {
    var res = []
    for await (let value of values) {
        res.push(value);
    }
    return res;
}

新增 ImageEditor.razor 组件button用来打开文件对话窗,canvas用来展示选择的图片

1
2
3
4
5
6
7
<PageTitle>ImageEditor</PageTitle>
<div>
  <button @onclick="OpenFilePicker" class="btn btn-primary">Open Image</button>
</div>
<div>
  <canvas @ref=CanvasReference width=@CanvasSize height=@CanvasSize></canvas>
</div>

新增 FileSystemAccessService用来封装文件操作,打开文件对话框需要利用浏览器的本地文件API如FileSystemAccessService中的方法ShowOpenFilePickerAsync打开文件对话框,其中OpenFilePickerOptions确定文件对话框打开的初始位置和能选中的文件类型

1
2
3
4
5
6
7
8
9
10
11
12
public async Task<FileSystemFileHandle[]> ShowOpenFilePickerAsync(OpenFilePickerOptions? openFilePickerOptions = null)
{
	IJSInProcessObjectReference helper = await moduleTask.Value;
	IJSObjectReference jSFileHandles = await jsRuntime.InvokeAsync<IJSObjectReference>("window.showOpenFilePicker",
			openFilePickerOptions?.Serializable());
	var length = await helper.InvokeAsync<int>("size", jSFileHandles);
	return await Task
			.WhenAll(Enumerable.Range(0, length)
			.Select(async i => new FileSystemFileHandle
			(await jSFileHandles.InvokeAsync<IJSObjectReference>("at", i), helper)
			).ToArray());
}

打开文件后在canvas标记中显示图片

1
2
3
4
5
6
7
8
9
10
11
public async Task DrawImage()
{
	if (JS2dContext == null) return;
	var imageWidth = await jSRuntime.InvokeAsync<double>("getAttribute", JSImageBitmap, "width");
	var imageHeight = await jSRuntime.InvokeAsync<double>("getAttribute", JSImageBitmap, "height");
	ImageDrawWidth = (imageWidth > imageHeight ? 1 : imageWidth / imageHeight) * CanvasSize;
	ImageDrawHeight = (imageWidth < imageHeight ? 1 : imageHeight / imageWidth) * CanvasSize;
	await jSRuntime.InvokeVoidAsync("setAttribute", JSCanvas, "width", ImageDrawWidth);
	await jSRuntime.InvokeVoidAsync("setAttribute", JSCanvas, "height", ImageDrawHeight);
	await JS2dContext.InvokeVoidAsync("drawImage", JSImageBitmap, 0, 0, ImageDrawWidth, ImageDrawHeight);
}

blazor-image-show

修改图片并保存在本地目录

在打开文件编辑文件并打开文件保存对话框保存修改后的文件,在ImageEditor组件中增加编辑选项和保存 button,当点击保存的时候调用save方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div class="row">
	<div class="form-group col">
			<label for="canvasSize">Canvas Size:</label>
			<input id="canvasSize" type="number" @oninput="WriteCanvasSize" class="form-control" value=@CanvasSize/>
	</div>
	 <div class="form-group col">
			<label for="topText">Top Text:</label>
			<input id="topText" @oninput="WriteTopText" class="form-control" />
	</div>
	<div class="form-group col">
			<label for="bottomText">Bottom Text:</label>
			<input id="bottomText" @oninput="WriteBottomText" class="form-control" />
	</div>
</div>
<br />
<button class="btn btn-primary" @onclick=Save>Save</button>
<br />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public async Task Save()
{
	FileSystemFileHandle? saveFileHandle = null;
	try
	{
			var options = new SaveFilePickerOptions()
			{
					StartIn = WellKnownDirectory.Pictures,
					SuggestedName = string.Concat("editor_", FileHandle.Name)
			};
			saveFileHandle = await FileSystemAccessService.ShowSaveFilePickerAsync(options);
	}
	catch (JSException jsex)
	{
			Console.WriteLine(jsex);
	}
	finally
	{
			if (saveFileHandle != null && JSCanvas != null)
			{
					var blobCallback = new BlobCallback(jSRuntime, await FileSystemAccessService.HelperAsync());
					blobCallback.Callback = async (BlobHandler blobhandler) =>
						 {
								 var writable = await saveFileHandle.CreateWritableAsync();
								 await writable.WriteAsync(blobhandler);
								 await writable.CloseAsync();
						 };
					await blobCallback.ToBlobAsync(JSCanvas);
			}
	}
}

在FileSystemAccessService中增加保存文件的对话框

1
2
3
4
5
6
7
public async Task<FileSystemFileHandle> ShowSaveFilePickerAsync(SaveFilePickerOptions? saveFilePickerOptions = null)
{
	IJSInProcessObjectReference? helper = await moduleTask.Value;
	IJSObjectReference? jSFileHandle = await jsRuntime.InvokeAsync<IJSObjectReference>("window.showSaveFilePicker",
			saveFilePickerOptions?.Serializable());
	return new FileSystemFileHandle(jSFileHandle, helper);
}

blazor-save-image

读取文件目录

在ImageEditor组件中增加打开目录button,当点击打开目录时调用OpenDictoryPicker方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div>
    <button @onclick="OpenDictoryPicker" class="btn btn-primary">Open Directory</button>
</div>
<table class="table">
    <thead>
        <tr>
            <th>File Name</th>
            <th>File Type</th>
            <th>Size</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var fileModel in RootDictoryModel.Children)
        {
            <tr>
                <td>@fileModel.Name</td>
                <td>@fileModel.Kind</td>
                <td>@ReadableByteSize(@fileModel.Size)</td>
            </tr>
        }
    </tbody>
</table>

OpenDictoryPicker方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected async Task OpenDictoryPicker()
{
	FileSystemDirectoryHandle? directoryHandle = null;
	try
	{
			var options = new DirectoryPickerOptions() { StartIn = WellKnownDirectory.Pictures };
			directoryHandle = await FileSystemAccessService.ShowDirectoryPickerAsync(options);
	}
	catch (JSException ex)
	{
			Console.WriteLine(ex);
	}
	finally
	{
			if (directoryHandle != null)
			{
					await BreadthFirstTraversal(directoryHandle);
			}
	}
}

blazor-open-directory

参考

Blazor File System Access

本文由作者按照 CC BY 4.0 进行授权
文章内容

Blazor WebAssembly 支持AOT

Blazor 文件上传