如何在ASP.NET Web API Swagger UI中实现文件夹拖拽上传?
ASP.NET Web API Swagger UI 文件夹拖拽上传实现方案
一、前端Swagger UI改造(支持文件夹选择/拖拽)
Swagger UI原生不支持文件夹上传,需自定义文件选择逻辑并递归读取文件夹内容:
1. 注入自定义脚本(针对Swashbuckle.AspNetCore)
在Program.cs的Swagger配置中注入自定义JS脚本,替换默认上传逻辑:
app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); // 注入自定义文件夹上传脚本 c.InjectJavascript("/js/swagger-folder-upload.js"); });
2. 编写自定义脚本swagger-folder-upload.js
修改Swagger的文件输入控件,添加文件夹选择支持,递归读取所有文件(含子目录)并记录相对路径:
document.addEventListener('DOMContentLoaded', function() { // 替换所有文件输入框,启用文件夹选择 const fileInputs = document.querySelectorAll('input[type="file"]'); fileInputs.forEach(input => { input.setAttribute('webkitdirectory', ''); input.setAttribute('directory', ''); input.setAttribute('multiple', ''); input.addEventListener('change', handleFileSelect); // 给父容器添加拖拽支持 const container = input.closest('.parameter__wrapper'); container.addEventListener('drop', handleDrop); container.addEventListener('dragover', (e) => e.preventDefault()); }); // 递归读取目录下的所有文件,保留相对路径 function traverseDirectory(entry, parentPath = '') { return new Promise((resolve) => { if (entry.isFile) { entry.file(file => { file.relativePath = parentPath + file.name; resolve([file]); }); } else if (entry.isDirectory) { const reader = entry.createReader(); reader.readEntries(entries => { Promise.all(entries.map(e => traverseDirectory(e, parentPath + entry.name + '/'))) .then(files => resolve([].concat(...files))); }); } }); } // 处理文件选择事件 async function handleFileSelect(e) { const files = await Promise.all(Array.from(e.target.files).map(file => { file.relativePath = file.name; return file; })); rewriteFormSubmit(files); } // 处理拖拽上传事件 async function handleDrop(e) { e.preventDefault(); const entries = Array.from(e.dataTransfer.items).map(item => item.webkitGetAsEntry()).filter(Boolean); const fileGroups = await Promise.all(entries.map(entry => traverseDirectory(entry))); const flatFiles = [].concat(...fileGroups); rewriteFormSubmit(flatFiles); } // 重写表单提交逻辑,携带文件和相对路径 function rewriteFormSubmit(files) { const form = document.querySelector('.swagger-ui form'); if (!form) return; form.addEventListener('submit', async function(e) { e.preventDefault(); const formData = new FormData(); // 保留原表单的其他参数 Array.from(form.elements).forEach(el => { if (el.name && el.type !== 'file') formData.append(el.name, el.value); }); // 添加文件和对应的相对路径 files.forEach(file => { formData.append('files', file); formData.append('relativePaths', file.relativePath); }); // 发送请求并更新Swagger响应区域 const res = await fetch(form.action, { method: form.method, body: formData, headers: { 'Accept': 'application/json' } }); const result = await res.json(); const responseArea = document.querySelector('.swagger-ui .response__body'); responseArea.innerHTML = `<pre>${JSON.stringify(result, null, 2)}</pre>`; }); } });
二、后端API处理(接收带路径的文件)
后端需同时接收文件和相对路径,以还原原始文件夹结构:
1. 编写上传接口
[ApiController] [Route("api/upload")] public class UploadController : ControllerBase { private readonly IWebHostEnvironment _env; public UploadController(IWebHostEnvironment env) { _env = env; } [HttpPost("folder")] public async Task<IActionResult> UploadFolder() { var files = Request.Form.Files; var relativePaths = Request.Form["relativePaths"].ToArray(); if (files.Count != relativePaths.Length) return BadRequest("文件与路径数量不匹配"); var saveRoot = Path.Combine(_env.WebRootPath, "folder-uploads"); Directory.CreateDirectory(saveRoot); for (int i = 0; i < files.Count; i++) { var file = files[i]; var targetPath = Path.Combine(saveRoot, relativePaths[i]); // 创建子目录(如果不存在) Directory.CreateDirectory(Path.GetDirectoryName(targetPath)); // 保存文件 using var stream = new FileStream(targetPath, FileMode.Create); await file.CopyToAsync(stream); } return Ok(new { TotalFiles = files.Count, Status = "上传完成" }); } }
2. 调整请求大小限制
在Program.cs中配置,避免大文件夹上传被拦截:
builder.Services.Configure<FormOptions>(options => { options.MultipartBodyLengthLimit = 1024 * 1024 * 1024; // 1GB,可按需调整 });
三、注意事项
- 浏览器兼容性:
webkitdirectory仅支持Chrome、Edge、Firefox等现代浏览器,IE不支持。 - 权限配置:确保后端保存目录有写入权限,避免文件保存失败。
- Swagger版本适配:旧版Swashbuckle的脚本注入方式可能不同,需对应调整。
内容的提问来源于stack exchange,提问作者Aryan
相关产品推荐
相关产品推荐

