MAUI Blazor学习12-文件另存为

MAUI Blazor学习12-文件另存为

 

MAUI Blazor系列目录

  1. MAUI Blazor学习1-移动客户端Shell布局 - SunnyTrudeau - 博客园 (cnblogs.com)
  2. MAUI Blazor学习2-创建移动客户端Razor页面 - SunnyTrudeau - 博客园 (cnblogs.com)
  3. MAUI Blazor学习3-绘制ECharts图表 - SunnyTrudeau - 博客园 (cnblogs.com)
  4. MAUI Blazor学习4-绘制BootstrapBlazor.Chart图表 - SunnyTrudeau - 博客园 (cnblogs.com)
  5. MAUI Blazor学习5-BLE低功耗蓝牙 - SunnyTrudeau - 博客园 (cnblogs.com)
  6. MAUI Blazor学习6-扫描二维码 - SunnyTrudeau - 博客园 (cnblogs.com)
  7. MAUI Blazor学习7-实现登录跳转页面 - SunnyTrudeau - 博客园 (cnblogs.com)
  8. MAUI Blazor学习8-支持多语言 - SunnyTrudeau - 博客园 (cnblogs.com)
  9. MAUI Blazor学习9-VS Code开发调试MAUI入门 - SunnyTrudeau - 博客园 (cnblogs.com)
  10. MAUI Blazor学习10-BarcodeScanner扫描二维码 - SunnyTrudeau - 博客园 (cnblogs.com)
  11. MAUI Blazor学习11-百度地图定位 - SunnyTrudeau - 博客园 (cnblogs.com)

 

 PC软件经常有处理本机文件的功能,需要打开文件,文件另存为,打开目录等功能,例如WinformWPFUWP都能很方便的调用Microsoft.Win32.SaveFileDialog实现文件另存为功能。MAUI是跨平台框架,不能简单的调用win32的组件。MAUI Blazor运行在浏览器里边,需要借助htmlJavaScript访问本机文件。本文研究一下实现文件另存为功能的不同方案。

 

继续沿用之前的MAUI Blazor项目MaBlaApp,增加文件另存为功能。

JavaScriptwindow.showSaveFilePicker方案

参考文档

Window:showSaveFilePicker() 方法 - Web API 接口参考 | MDN (mozilla.org)

js showOpenFilePicker showSaveFilePicker showDirectoryPicker API - Ajanuw - 博客园 (cnblogs.com)

 

编写JavaScript模块,把字符串另存为txt文件。

D:\Software\gitee\mauiblazorapp\MaBlaApp\wwwroot\js\FileDialog.js

//另存为txt文件
//https://developer.mozilla.org/zh-CN/docs/Web/API/Window/showSaveFilePicker
//https://developer.mozilla.org/zh-CN/docs/Web/API/FileSystemFileHandle
export async function SaveAsTxtFile(filename, contents) {

    //显示文件选择器,返回FileSystemFileHandle
    const fileHandle = await window.showSaveFilePicker({
        suggestedName: filename,
        types: [
            {
                description: "txt文件",
                accept: {
                    "text/plain": [".txt"]
                }
            }]
    });

    //创建一个 FileSystemWritableFileStream 用来写入
    const writable = await fileHandle.createWritable();

    //将文件内容写入到流中
    await writable.write(contents);

    //关闭文件并将内容写入磁盘
    await writable.close();
}

 

CommunityToolkitFileSaver.SaveAsync方案  

 

NET MAUI社区提供一个扩展组件来支持跨平台另存文件功能,开源的好处体现在这里,官方没有做好的,社区能弥补一下。

 

FileSaver - .NET MAUI 社区工具包 - Community Toolkits for .NET | Microsoft Learn 

参考官网的DEMO写代码即可,非常简单。

D:\Software\gitee\mauiblazorapp\MaBlaApp\Pages\FileDialog.razor

//另存为txt文件(CommunityToolkit)
    private async Task SaveAsTxtFileByToolkit()
    {
        cts = new CancellationTokenSource();
        CancellationToken cancellationToken = cts.Token;

        string filename = $"{DateTimeOffset.Now:yyyyMMdd-HHmmss}.txt";

        using var stream = new MemoryStream(Encoding.UTF8.GetBytes(message));

        var fileSaverResult = await fileSaver.SaveAsync(filename, stream, cancellationToken);

        if (fileSaverResult.IsSuccessful)
        {
            await Toast.Make($"The file was saved successfully to location: {fileSaverResult.FilePath}").Show(cancellationToken);
        }
        else
        {
            await Toast.Make($"The file was not saved successfully with error: {fileSaverResult.Exception.Message}").Show(cancellationToken);
        }
    }

 

Razor页面调用文件另存为

 

编写一个页面,采用隔离JavaScript组件的方案调用JavaScript模块的SaveAsTxtFile,直接调用CommunityToolkit的的方案。

 

D:\Software\gitee\mauiblazorapp\MaBlaApp\Pages\FileDialog.razor

@page "/filedialog"
@implements IAsyncDisposable
@inject IJSRuntime JS
@using CommunityToolkit.Maui.Alerts;
@using CommunityToolkit.Maui.Storage
@using System.Text;
@inject IFileSaver fileSaver

<h3>打开本机文件和目录对话框</h3>

<ul class="list-group">
    <li class="list-group-item">
        <button type="button" class="btn btn-primary btn-sm" @onclick=SaveAsTxtFileByJavaScript>另存为txt文件(JavaScript)</button>
    </li>
    <li class="list-group-item">
        <button type="button" class="btn btn-primary btn-sm" @onclick=SaveAsTxtFileByToolkit>另存为txt文件(CommunityToolkit)</button>
    </li>
</ul>
<textarea class="m-2" style="height:100px;" @bind=message></textarea>

@code {

    private IJSObjectReference? module;
    private string message = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}, 你好,MAUI Blaor";
    private CancellationTokenSource? cts;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        base.OnAfterRender(firstRender);

        if (firstRender)
        {
            module = await JS.InvokeAsync<IJSObjectReference>("import", "./js/filedialog.js");
        }
    }

    //另存为txt文件(JavaScript)
    private async void SaveAsTxtFileByJavaScript()
    {
        string filename = $"{DateTimeOffset.Now:yyyyMMdd-HHmmss}.txt";

        try
        {
            //另存为txt文件,默认utf8
            await module!.InvokeVoidAsync("SaveAsTxtFile", filename, message);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }

    //另存为txt文件(CommunityToolkit)
    private async Task SaveAsTxtFileByToolkit()
    {
        cts = new CancellationTokenSource();
        CancellationToken cancellationToken = cts.Token;

        string filename = $"{DateTimeOffset.Now:yyyyMMdd-HHmmss}.txt";

        using var stream = new MemoryStream(Encoding.UTF8.GetBytes(message));

        var fileSaverResult = await fileSaver.SaveAsync(filename, stream, cancellationToken);

        if (fileSaverResult.IsSuccessful)
        {
            await Toast.Make($"The file was saved successfully to location: {fileSaverResult.FilePath}").Show(cancellationToken);
        }
        else
        {
            await Toast.Make($"The file was not saved successfully with error: {fileSaverResult.Exception.Message}").Show(cancellationToken);
        }
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            await module.DisposeAsync();
        }
    }
}

 

测试

windows上面使用VS2022调试运行,功能都是可以实现的,但是JavaScriptSaveAsTxtFile有时候闪退,CommunityToolkit的方案比较稳定,也可能是我的JavaScript代码不完善,水平有限。

考虑到手机APP一般没有读写本机文件的应用场景,所以不再手机上测试了。

 

结论

推荐用CommunityToolkit的文件另存为方案。

 

DEMO代码地址:https://gitee.com/woodsun/mauiblazorapp

 

posted on 2023-11-05 21:57  SunnyTrudeau  阅读(358)  评论(0编辑  收藏  举报