MAUI Blazor 加载本地图片的解决方案
前言
为了解决MAUI Blazor无法加载本地图片,https://github.com/dotnet/maui/issues/2907,所以写了这篇文章。
有token大佬珠玉在前,https://www.cnblogs.com/hejiale010426/p/17073079.html ,以及微软文档的补充,https://learn.microsoft.com/zh-cn/aspnet/core/blazor/images?view=aspnetcore-7.0#stream-image-data,才能写出这篇文章,特此感谢。
解决的思路是判断路径是本机路径,如果是,调用js为它生成blob,将本机路径与blob的url对应上通过字典存储起来,主动释放时从字典移除。
原理上与token大佬的文章没有什么太大区别,最主要的是增加了缓存机制
正文
添加所需代码
- 添加接口ILocalImageService
//https://github.com/dotnet/maui/issues/2907
public interface ILocalImageService
{
// 为本地路径的图片创建blob,并返回blob的url,若不是本地路径会直接返回
Task<string> ToUrl(string path);
// 调用js,释放图片的blob
Task RevokeUrl(string path);
}
- 添加实现类
public class LocalImageService : ILocalImageService
{
private readonly IJSRuntime JS;
private IJSObjectReference module = default!;
//存储已生成的图片,将图片本机路径与图片blob的url联系起来
private static readonly ConcurrentDictionary<string, string> urls = new();
//控制访问单个图片资源的线程数量,若图片blob在生成中,将等待
private static readonly ConcurrentDictionary<string, SemaphoreSlim> semaphores = new();
public LocalImageService(IJSRuntime jS)
{
JS = jS;
}
//为本地路径的图片创建blob,并返回blob的url,若不是本地路径会直接返回
public async Task<string> ToUrl(string path)
{
await InitModule();
if (!File.Exists(path))
{
return path;
}
SemaphoreSlim semaphore = semaphores.GetOrAdd(path, _ => new SemaphoreSlim(1));
await semaphore.WaitAsync();
try
{
if (urls.TryGetValue(path, out string? url))
{
return url;
}
else
{
string newUrl = await GenerateImageUrl(path);
urls.TryAdd(path, newUrl);
return newUrl;
}
}
finally
{
semaphore.Release();
}
}
//调用js,生成图片的blob
private async Task<string> GenerateImageUrl(string path)
{
using var imageStream = File.OpenRead(path);
var dotnetImageStream = new DotNetStreamReference(imageStream);
var url = await module.InvokeAsync<string>("streamToUrl", new object[1] { dotnetImageStream });
return url;
}
//调用js,释放图片的blob
public async Task RevokeUrl(string path)
{
await InitModule();
if(string.IsNullOrWhiteSpace(path))
{
return;
}
if (urls.ContainsKey(path))
{
urls.TryRemove(path, out string? url);
await module.InvokeVoidAsync("revokeUrl", new object[1] { url! });
}
}
//初始化JS模块
private async Task InitModule()
{
module ??= await JS!.InvokeAsync<IJSObjectReference>("import", "./js/getLocalImage.js");
}
}
- 在wwwroot/js中添加getLocalImage.js
/*出自 https://www.cnblogs.com/hejiale010426/p/17073079.html,有修改*/
/** 将stream转url对象 */
export async function streamToUrl(imageStream) {
// 适配webview和web
const arrayBuffer = await imageStream.arrayBuffer();
const blob = new Blob([arrayBuffer]);
return (window.URL || window.webkitURL || window || {}).createObjectURL(blob);
}
/**
* 释放url对象,因为createObjectURL创建的对象一直会存在可能会占用过多的内存,请注意释放
*/
export function revokeUrl(url) {
(window.URL || window.webkitURL || window || {}).revokeObjectURL(url);
}
- 在MauiProgram.cs中添加以下代码
builder.Services.AddScoped<ILocalImageService, LocalImageService>();
注意事项
使用时一定要注意释放,当你不需要这个图片时主动释放它。如果它需要经常显示,那么不必释放,因为生成比较大的图片在安卓上是比较慢的。
最终效果


源码
源码放在GitHub上了,https://github.com/Yu-Core/MAUIBlazorLoadLocalImage
标签:
MAUIBlazor
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· spring官宣接入deepseek,真的太香了~