Как отобразить локальное изображение, а также изображение ресурсов в .Net MAUI Blazor
В .Net MAUI Blazor я могу использовать тег img для отображения изображения из папки wwwroot. Но как вывести изображение из внутреннего хранилища устройства? А как выводить изображения из ресурсов приложения?
5 ответов
Из внутренней памяти
Мы можем прочитать это вbytes
и преобразовать его вbase64
string , затем показать на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" />
Я решил таким образом.
- Добавьте изображение png в Resources\Raw и установите тип компиляции MauiAsset.
- Проверьте файл проекта, чтобы изображение не было исключено через 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);
}
}
Лучшего решения я не нашел. Кажется, слишком мало информации по этой теме.