基于.NET Core 7的Blazor Server应用如何实现云盘式文件夹上传?
实现Blazor Server直接文件夹上传的方案
核心思路
利用浏览器原生的directory(兼容旧版Chrome的webkitdirectory)属性让文件选择器支持文件夹选择,通过JS递归遍历文件夹内的所有文件并保留层级路径,再将文件和路径信息传递给Blazor后端,最终在服务器端重建文件夹结构完成上传。
具体实现步骤
1. 编写JS交互脚本
在wwwroot/js目录下新建folderUpload.js,写入以下代码:
window.folderUpload = { init: function (elementId, dotNetRef) { const input = document.getElementById(elementId); // 开启文件夹选择支持 input.setAttribute('directory', ''); input.setAttribute('webkitdirectory', ''); input.setAttribute('multiple', ''); input.addEventListener('change', async (e) => { const fileEntries = []; // 递归遍历文件夹,收集文件及相对路径 async function traverseFileTree(item, parentPath = '') { if (item.isFile) { const file = await item.getFile(); fileEntries.push({ file: file, relativePath: parentPath + file.name }); } else if (item.isDirectory) { const dirReader = item.createReader(); const entries = await dirReader.readEntries(); const currentPath = parentPath + item.name + '/'; for (const entry of entries) { await traverseFileTree(entry, currentPath); } } } // 处理选中的文件夹条目 for (const file of e.target.files) { const entry = await window.webkitResolveLocalFileSystemURL(file.webkitRelativePath); await traverseFileTree(entry); } // 组装FormData传递给Blazor const formData = new FormData(); fileEntries.forEach(entry => { formData.append('uploadFiles', entry.file, entry.relativePath); }); // 调用Blazor后端方法处理上传 await dotNetRef.invokeMethodAsync('ProcessFolderUpload', formData); }); } };
2. Blazor组件集成实现
以FileManager.razor组件为例,添加以下内容:
2.1 引入依赖与脚本
@inject IJSRuntime JSRuntime @implements IAsyncDisposable <script src="js/folderUpload.js"></script>
2.2 UI与交互逻辑
<!-- 隐藏原生文件选择框,用自定义按钮触发 --> <input type="file" id="folderUploadInput" style="display: none;" /> <button @onclick="OpenFolderSelector" class="btn btn-primary">选择文件夹上传</button> @code { private DotNetObjectReference<FileManager>? _dotNetReference; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { _dotNetReference = DotNetObjectReference.Create(this); // 初始化文件夹上传逻辑 await JSRuntime.InvokeVoidAsync("folderUpload.init", "folderUploadInput", _dotNetReference); } await base.OnAfterRenderAsync(firstRender); } private void OpenFolderSelector() { // 触发隐藏的文件选择框 var input = document.getElementById("folderUploadInput"); input.click(); } [JSInvokable] public async Task ProcessFolderUpload(IFormFileCollection uploadFiles) { // 服务器存储根路径,可根据实际需求调整 var rootSavePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "user-uploads"); foreach (var file in uploadFiles) { // 根据相对路径拼接完整存储路径 var fullSavePath = Path.Combine(rootSavePath, file.FileName); // 创建文件所在的目录(如果不存在) var directoryPath = Path.GetDirectoryName(fullSavePath); if (!Directory.Exists(directoryPath)) { Directory.CreateDirectory(directoryPath); } // 写入文件到服务器 using var fileStream = new FileStream(fullSavePath, FileMode.Create); await file.CopyToAsync(fileStream); } // 上传完成后可添加刷新文件列表、提示用户等逻辑 StateHasChanged(); } // 释放DotNet对象引用 public async ValueTask DisposeAsync() { _dotNetReference?.Dispose(); await Task.CompletedTask; } }
3. 关键配置与注意事项
- 调整请求大小限制:如果上传大文件夹,需要在
Program.cs中修改表单请求限制:builder.Services.Configure<FormOptions>(options => { options.MultipartBodyLengthLimit = 1073741824; // 设置为1GB,根据实际需求调整 }); - 浏览器兼容性:
directory属性已支持Chrome、Edge、Firefox 50+等主流浏览器,webkitdirectory用于兼容旧版Chrome,两者同时设置可覆盖更多场景。 - 权限控制:确保服务器存储目录有读写权限,同时结合内部应用的用户权限体系,避免越权上传操作。
内容的提问来源于stack exchange,提问作者Lash
相关产品推荐
相关产品推荐

