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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 21:45:29