You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MAUI中如何重复使用Camera.Maui获取的ImageSource?

问题根源分析

Camera.Maui的GetSnapShot()返回的ImageSource基于一次性可读流实现:当流被首次渲染(比如PreviewPage的ImageButton显示)后,流指针会走到末尾并被自动关闭,后续再尝试读取(比如ImagePage中设置Image.Source)就会触发“流已关闭”错误,直接保存这类流也会失败。

解决方案

核心思路是将相机返回的临时流内容持久化到内存(byte[])或本地临时文件,实现多次安全访问,避免流依赖问题。

方案1:转为byte[]存储(适合中小尺寸图片)

修改快照存储逻辑,将一次性流转为可重复使用的byte[]数组:

MainPage.xaml.cs 修改

using Camera.MAUI;
using System.IO;

private readonly CameraView camera;
private readonly List<byte[]> picBytesList = new(); // 替换原List<ImageSource>

public MainPage()
{
    InitializeComponent();
    camera = cameraView;
}

public async void GrabSnapshot()
{
    var snapshotSource = camera.GetSnapShot();
    if (snapshotSource is StreamImageSource streamSource)
    {
        using var stream = await streamSource.Stream(CancellationToken.None);
        using var memoryStream = new MemoryStream();
        await stream.CopyToAsync(memoryStream);
        picBytesList.Add(memoryStream.ToArray());
    }
}

PreviewPage.xaml.cs 修改

private void DisplayPics(List<byte[]> picBytesList)
{
    int counter = 0;
    // 假设已完成GridLayout的行列定义
    foreach (var bytes in picBytesList)
    {
        int row = (int)Math.Floor(counter / 2m);
        int column = counter % 2;
        // 从byte[]生成可重复使用的ImageSource
        var thumbSource = ImageSource.FromStream(() => new MemoryStream(bytes));
        var button = new ImageButton 
        { 
            Source = thumbSource,
            HeightRequest = 150, // 固定缩略图尺寸减少内存占用
            WidthRequest = 200
        };
        // 绑定跳转事件
        button.Clicked += (s, e) => 
        {
            Navigation.PushAsync(new ImagePage(counter, picBytesList));
        };
        GridLayout.Add(button, column, row);
        counter++;
    }
}

ImagePage.xaml.cs 修改

private readonly List<byte[]> _picBytesList;
private int _currentIndex;

public ImagePage(int indexParam, List<byte[]> picBytesList)
{
    InitializeComponent();
    _picBytesList = picBytesList;
    _currentIndex = indexParam;
    LoadCurrentImage();

    // 添加左右切换逻辑(示例)
    AddNavigationButtons();
}

private void LoadCurrentImage()
{
    var bytes = _picBytesList[_currentIndex];
    ImagePanel.Source = ImageSource.FromStream(() => new MemoryStream(bytes));
}

private void AddNavigationButtons()
{
    var prevBtn = new Button { Text = "←", HorizontalOptions = LayoutOptions.Start };
    prevBtn.Clicked += (s, e) =>
    {
        if (_currentIndex > 0)
        {
            _currentIndex--;
            LoadCurrentImage();
        }
    };

    var nextBtn = new Button { Text = "→", HorizontalOptions = LayoutOptions.End };
    nextBtn.Clicked += (s, e) =>
    {
        if (_currentIndex < _picBytesList.Count - 1)
        {
            _currentIndex++;
            LoadCurrentImage();
        }
    };

    // 将按钮添加到页面布局(需根据你的XAML结构调整)
    var buttonLayout = new HorizontalStackLayout { HorizontalOptions = LayoutOptions.Center };
    buttonLayout.Add(prevBtn);
    buttonLayout.Add(nextBtn);
    Content = new VerticalStackLayout { Children = { ImagePanel, buttonLayout } };
}

方案2:保存到本地临时文件(适合大尺寸图片,优化内存)

针对4160x3120的JPG,20-50张的byte[]会占用较多内存,可选择保存到APP临时目录,用文件路径加载图片:

MainPage.xaml.cs 修改

using Camera.MAUI;
using System.IO;
using Microsoft.Maui.Storage;

private readonly CameraView camera;
private readonly List<string> tempFilePaths = new();

public async void GrabSnapshot()
{
    var snapshotSource = camera.GetSnapShot();
    if (snapshotSource is StreamImageSource streamSource)
    {
        using var stream = await streamSource.Stream(CancellationToken.None);
        // 创建临时文件
        var tempDir = FileSystem.CacheDirectory;
        var tempFileName = $"snapshot_{Guid.NewGuid()}.jpg";
        var tempPath = Path.Combine(tempDir, tempFileName);
        
        using var fileStream = File.OpenWrite(tempPath);
        await stream.CopyToAsync(fileStream);
        tempFilePaths.Add(tempPath);
    }
}

临时文件清理

在用户确认保存或APP退出时清理临时文件:

private void CleanupTempFiles()
{
    foreach (var path in tempFilePaths)
    {
        if (File.Exists(path)) File.Delete(path);
    }
    tempFilePaths.Clear();
}
内存优化建议
  1. 指定JPG格式:检查Camera.Maui配置,将快照格式设为JPG(替代默认PNG),大幅降低文件体积。
  2. 生成缩略图:在PreviewPage中对原图进行缩放处理,生成小尺寸缩略图,减少UI渲染内存占用:
    public byte[] ResizeImage(byte[] imageBytes, int targetWidth, int targetHeight)
    {
        using var stream = new MemoryStream(imageBytes);
        using var originalImage = PlatformImage.FromStream(stream);
        using var resizedImage = originalImage.Resize(targetWidth, targetHeight, ResizeMode.AspectFill);
        using var resizedStream = new MemoryStream();
        resizedImage.Save(resizedStream, ImageFormat.Jpeg);
        return resizedStream.ToArray();
    }
    
  3. 页面销毁时释放资源:在PreviewPage和ImagePage的OnDisappearing方法中清空Image控件Source,避免内存泄漏:
    protected override void OnDisappearing()
    {
        base.OnDisappearing();
        foreach (var child in GridLayout.Children)
        {
            if (child is ImageButton btn) btn.Source = null;
        }
        ImagePanel.Source = null;
    }
    

内容的提问来源于stack exchange,提问作者computercarguy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 14:54:54