Modal复用InputFile组件上传文件时触发JSInterop异常求助
抛出异常:Microsoft.JSInterop.JSException: There is no file with ID 6. The file list may have changed. Error: There is no file with ID 6
触发场景:打开包含两个InputFile的模态框,关闭后重新打开,在其中一个InputFile上传文件时触发该错误。两个InputFile分别位于独立的ProductImageUploaderComponent组件中。
模态框代码:
<div class="product-portrait-container"> <div class="product-portrait-image-container"> <p>@Localize["Product_Background_Image"]</p> <div class="product-image-div"> <ProductImageUploaderComponent @ref="_backgroundImageUploader" IsPortrait="_isPortrait" NotifyParent="NotifyProductCreationModal" AllowedFileExtensions="@Constants.ALLOWED_PRODUCT_IMAGE_FILE_EXTENSIONS" IsEditMode="@_isEditMode"></ProductImageUploaderComponent> </div> </div> <div class="product-portrait-image-container"> <p>@Localize["Product_Image_Foreground"]</p> <div class="product-image-div"> <ProductImageUploaderComponent @ref="_imageForegroundUploader" IsPortrait="_isPortrait" NotifyParent="NotifyProductCreationModal" AllowedFileExtensions="@allowedImageForegroundFileExtension" IsEditMode="@_isEditMode"></ProductImageUploaderComponent> </div> </div> </div>
ProductImageUploaderComponent核心代码:
<InputFile id="@($"fileInput{ComponentId}")" type="file" name="file" accept="@AllowedFileExtensions" style="display: none" @ref="InputFile" OnChange="UploadImage" />
1. 用条件渲染销毁/重建模态框组件
避免用CSS隐藏模态框(比如display: none),改用@if条件渲染控制模态框的存在:
@if (_showModal) { <!-- 模态框内容 --> <div class="product-portrait-container"> <!-- ... 原有组件代码 ... --> </div> }
关闭模态框时设置_showModal = false,这样内部的ProductImageUploaderComponent会被彻底销毁,重新打开时会创建全新实例,避免旧的InputFile引用残留。
2. 在组件销毁时清理InputFile状态
让ProductImageUploaderComponent实现IDisposable接口,清理InputFile引用并重置文件输入:
public class ProductImageUploaderComponent : ComponentBase, IDisposable { private ElementReference _inputFile; [Inject] private IJSRuntime JSRuntime { get; set; } // ... 其他属性和方法 ... public void Dispose() { // 清空文件输入的value,避免残留旧文件ID _ = JSRuntime.InvokeVoidAsync("clearFileInput", _inputFile); } }
在全局JS文件中添加clearFileInput函数:
function clearFileInput(element) { element.value = ''; }
3. 确保ComponentId全局唯一
每次创建ProductImageUploaderComponent时生成新的ComponentId,避免重复ID导致JS元素选择冲突:
public class ProductImageUploaderComponent : ComponentBase { public string ComponentId { get; set; } = Guid.NewGuid().ToString("N"); // ... 其他代码 ... }
4. 重置InputFile引用和上传状态
在ProductImageUploaderComponent的OnParametersSetAsync方法中重置上传相关状态,确保组件重新激活时状态干净:
protected override async Task OnParametersSetAsync() { await base.OnParametersSetAsync(); // 清空文件输入 if (_inputFile.Context != null) { await JSRuntime.InvokeVoidAsync("clearFileInput", _inputFile); } // 重置其他上传状态(比如已选文件、上传进度等) _selectedFile = null; _uploadProgress = 0; }
这个错误本质是Blazor的InputFile组件依赖JS维护的文件列表,当模态框关闭后组件未被销毁,重新打开时旧的组件实例引用依然存在,JS侧的文件列表已被清空,但Blazor侧仍尝试引用旧的文件ID,导致不匹配。通过销毁组件、重置状态、确保唯一ID,可以彻底解决这个问题。
内容的提问来源于stack exchange,提问作者Arnaud VDR

