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

Blazor中HTML video标签锁定视频文件无法移动如何解决

Blazor中video标签锁定本地文件导致无法移动的解决方案

问题场景

在Blazor Dialog组件中展示磁盘目录存储的视频文件,选中目标视频后点击「Upload」按钮上传至服务器,上传完成后需要将源文件移动至归档目录。执行移动操作时系统提示文件被其他进程占用,确认是<video>标签加载视频时持有文件锁导致。

初始实现的Razor代码如下:

<RadzenListBox TValue="FileDto"
               @ref=FileListBox
               Data=@TakesList>
    <Template Context="takeIn">
        <RadzenCard>
            @{
                var take = takeIn as FileDto;
            }
            <div class="row">
                <RadzenCheckBox TValue="bool" @bind-Value=take.IsSelectedForUpload class="col-auto"/>
                <div class="col-auto">
                    <video class="w-25" controls @ref=@take.VideoReference>
                        <source src=@($"{@take.FileName}#t=9.5") type="video/mp4">
                            Your browser does not support the video tag.
                    </video>
                </div>
            </div>
            <RadzenProgressBar Max=100 Value=@take.PercentageFinished Visible=@(take.PercentageFinished > 0) />
        </RadzenCard>
    </Template>
</RadzenListBox>

补充说明

最初尝试改为列表中展示视频缩略图,避免直接加载完整视频锁定文件,该方案可解决列表页的文件锁定问题。后续新增独立Dialog组件承载视频播放器,用于单独播放选中的视频,原本预期关闭Dialog后文件锁会自动释放,但实际测试发现:播放完视频、关闭Dialog后,文件仍然处于锁定状态,无法执行移动操作。


可行解决方案

方案1:主动调用JS接口强制释放video持有的文件锁

浏览器不会在video元素隐藏、甚至Dialog关闭DOM移除后立刻释放文件句柄,必须主动触发媒体资源卸载流程,在关闭Dialog、执行文件移动操作前,通过IJSRuntime调用以下JS方法,传入video元素的引用:

function releaseVideoFileLock(videoEl) {
    if (!videoEl) return;
    // 先暂停播放
    videoEl.pause();
    // 移除所有source子节点
    while (videoEl.firstChild) {
        videoEl.removeChild(videoEl.firstChild);
    }
    // 移除src属性
    videoEl.removeAttribute('src');
    // 调用load方法强制卸载原有资源,释放文件锁
    videoEl.load();
}

注意:不要只设置videoEl.src = ''或者赋值空白地址,部分浏览器会将空地址解析为当前页面地址,不会触发资源释放,必须调用load()方法才能完成卸载。

方案2:改用后端流接口返回视频,从根源避免文件锁定

直接将本地物理路径作为video的src时,浏览器会直接打开本地文件句柄读取内容,锁的释放时机完全不可控。可以在后端新增一个视频访问接口,打开文件时指定共享读写/删除权限,就算浏览器持有连接,也不会阻塞本地文件移动操作:

// 后端接口示例(Program.cs中注册)
app.MapGet("/api/video/play", async (string fileName, IWebHostEnvironment env) =>
{
    // 必须做路径合法性校验,防止路径遍历攻击
    var videoRootPath = Path.Combine(env.ContentRootPath, "你的视频存储目录");
    var fullPath = Path.GetFullPath(Path.Combine(videoRootPath, fileName));
    if (!fullPath.StartsWith(videoRootPath) || !File.Exists(fullPath))
    {
        return Results.NotFound();
    }
    // 打开文件时设置共享模式,允许其他进程读写、删除、移动文件
    var fileStream = new FileStream(
        fullPath, 
        FileMode.Open, 
        FileAccess.Read, 
        FileShare.ReadWrite | FileShare.Delete,
        bufferSize: 4096,
        useAsync: true);
    // 开启范围处理,支持视频拖动进度条
    return Results.File(fileStream, "video/mp4", enableRangeProcessing: true);
});

前端将video的src改为指向该接口即可:

<source src=@($"/api/video/play?fileName={take.FileName}#t=9.5") type="video/mp4">

这个方案稳定性最高,不需要额外处理锁释放逻辑,就算视频正在播放,也可以正常执行文件移动、删除操作。

方案3:增加重试逻辑作为兜底

部分浏览器内核(尤其是旧版Chromium)就算主动调用了释放方法,也可能存在几十到上百毫秒的锁释放延迟,可以在执行文件移动操作时增加重试机制,最多重试3次,每次间隔200ms,覆盖偶发的锁未释放场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 09:54:23