Как отобразить локальное изображение, а также изображение ресурсов в .Net MAUI Blazor

В .Net MAUI Blazor я могу использовать тег img для отображения изображения из папки wwwroot. Но как вывести изображение из внутреннего хранилища устройства? А как выводить изображения из ресурсов приложения?

5 ответов

Из внутренней памяти

Мы можем прочитать это вbytesи преобразовать его вbase64string , затем показать наimgярлык .

Учитывая, что мы поместили изображение под названиемdog.pngвFileSystem.CacheDirectoryпапка.

Образец кода

      @if (imageSource is not null)
{
    <div>
        <img src="@imageSource" width="200" height="200" />
    </div>
}

@code {
    private string? imageSource;

    protected override void OnInitialized()
    {
        var newFile = Path.Combine(FileSystem.CacheDirectory, "dog.png");
        var imageBytes  = File.ReadAllBytes(newFile);
        imageSource = Convert.ToBase64String(imageBytes);
        imageSource = string.Format("data:image/png;base64,{0}", imageSource);
    }
}

Из моего исследования: на самом деле вы можете получить путь к папке wwwroot в приложении бритвы с помощью: . В Windows вы можете добавлять файлы в эту папку, которые будут доступны из Blazor HTML. Однако в Android папка wwwroot встроена в приложение и не будет доступна (AppDomain.CurrentDomain.BaseDirectoryвернуть пустую папку).

После просмотра репозитория .NET MAUI github вBlazorWebViewкласс, который я нашел:

      public virtual IFileProvider CreateFileProvider(string contentRootDir)
{
    // Call into the platform-specific code to get that platform's asset file provider
    return ((BlazorWebViewHandler)(Handler!)).CreateFileProvider(contentRootDir);
}

Который можно использовать для передачи файлов в Blazor. Например, если вы хотите сделать доступными все файлы из AppDataDirectory:

      public class CustomFilesBlazorWebView : BlazorWebView
{
    public override IFileProvider CreateFileProvider(string contentRootDir)
    {
        var lPhysicalFiles = new PhysicalFileProvider(FileSystem.Current.AppDataDirectory);
        return new CompositeFileProvider(lPhysicalFiles, base.CreateFileProvider(contentRootDir));
    }
}

Затем вMainPage.xaml:

      <local:CustomFilesBlazorWebView HostPage="wwwroot/index.html" x:Name="WebView">
    <BlazorWebView.RootComponents>
        <RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
    </BlazorWebView.RootComponents>
</local:CustomFilesBlazorWebView>

Например, если в AppDataDirectory у вас есть файлimages/user.pngв любом компоненте Blazor вы можете использовать:

      <img src="images/user.png" />

Я решил таким образом.

  1. Добавьте изображение png в Resources\Raw и установите тип компиляции MauiAsset.
  2. Проверьте файл проекта, чтобы изображение не было исключено через ItemGroup-> None Remove. В этом случае удалите группу элементов, связанную с изображением.

После этого:

В моем компоненте бритвы HTML

       <img src="@imageSource">

В части кода:

      private string? imageSource;
protected override async Task OnInitializedAsync()
{
    try
    {
        using var stream = 
            await FileSystem.OpenAppPackageFileAsync("testimage.png");
        using var reader = new StreamReader(stream);
        byte[] result;
        using (var streamReader = new MemoryStream())
        {
            stream.CopyTo(streamReader);
            result = streamReader.ToArray();
        }
        imageSource = Convert.ToBase64String(result);
        imageSource = string.Format("data:image/png;base64,{0}", imageSource);
    }
    catch (Exception ex)
    {
        //log error
    }

}

Для отображения из ресурса см. … Blazor Hybrid static Files/.Net Maui :

  • Добавьте файл в проект в папку с именем Resources/Raw.
  • Убедитесь, что файл/Свойства/Действие сборки = MauiAsset.
  • Создайте компонент бритвы, который:
    • ЗвонкиMicrosoft.Maui.Storage.FileSystem.OpenAppPackageFileAsyncчтобы получить поток для ресурса.
    • Читает поток с помощью StreamReader.
    • Вызывает StreamReader.ReadToEndAsync для чтения файла.

Пример кода бритвы (из этой ссылки):

      @page "/static-asset-example"
@using System.IO
@using Microsoft.Extensions.Logging
@using Microsoft.Maui.Storage
@inject ILogger<StaticAssetExample> Logger

<h1>Static Asset Example</h1>

<p>@dataResourceText</p>

@code {
    public string dataResourceText = "Loading resource ...";

    protected override async Task OnInitializedAsync()
    {
        try
        {
            using var stream = 
                await FileSystem.OpenAppPackageFileAsync("Data.txt");
            using var reader = new StreamReader(stream);

            dataResourceText = await reader.ReadToEndAsync();
        }
        catch (FileNotFoundException ex)
        {
            dataResourceText = "Data file not found.";
            Logger.LogError(ex, "'Resource/Raw/Data.txt' not found.");
        }
    }
}

Чтобы получить доступ к локальному файлу (не активу в ресурсах) из кода бритвы, вам понадобитсяserviceкоторый с учетом имени файла (или относительного пути) возвращает содержимое файла в виде потока.

Я не нахожу документ, говорящий, как сделать это для Мауи, а затем внедрить это в код бритвы.

Такая служба будет использовать помощники файловой системы .Net для доступа к файлу. Это будет похоже на приведенный выше пример MauiAsset, но с использованием одного из помощников пути, а НЕ с вызовом OpenAppPackageFileAsync.

TBD - кто-нибудь даст ссылку или пример?

Я пробовал разные методы. Подход Base64 имеет серьезные проблемы с памятью, поскольку всего несколько изображений с высоким разрешением могут привести к сбою приложения. Другой метод с использованием staticFiles работает в Windows для записи изображений, но в Android каталог wwwroot находится внутри пакета, и туда невозможно записывать изображения. В конце концов я применил подход Object URL, который лучше с точки зрения производительности. Однако это означает, что мне придется либо копировать изображения в приложение (например, с помощью SQLite), либо сохранять внешние адреса, а затем каждый раз читать поток для отображения изображений. Фрагмент кода для отображения изображений выглядит следующим образом:

      ...
    <MudPaper Class="pa-3" Elevation="3">
        @if (_objUrl is null)
        {
            <MudAlert Severity="Severity.Normal">Choose an Image</MudAlert>
        }
        else
        {
            <MudImage @ref="_image" Height="400" Alt="pic" Class="rounded-lg" Src="@_objUrl" @onload="Revoke" />
        }
    </MudPaper>
...

<script>
    window.createObjectURL = async (imageStream) => {
        const arrayBuffer = await imageStream.arrayBuffer();
        const blob = new Blob([arrayBuffer]);
        return URL.createObjectURL(blob);
    }

    window.revokeObjectURL = (url) => {
        URL.revokeObjectURL(url); 
    }
</script>

@code
{
    private MudImage _image;
    private string? _objUrl;

    private async Task Revoke(ProgressEventArgs args)
    {
        var imageSrc = _image.Src;
        if (imageSrc is not null)
        {
            await JS.InvokeVoidAsync("revokeObjectURL", imageSrc);
        }
    }

    private async Task UploadFiles(IBrowserFile file)
    {
        Debug.WriteLine($"size:{file.Size}");
        await using var openReadStream = file.OpenReadStream(file.Size);
        var dotnetImageStream = new DotNetStreamReference(openReadStream);
        _objUrl = await JS.InvokeAsync<string>("createObjectURL", dotnetImageStream);
    }
}

Лучшего решения я не нашел. Кажется, слишком мало информации по этой теме.

Другие вопросы по тегам