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

C#实现从页面获取文件夹层级并保存至数据库的方案

解决方案:获取完整文件夹层级或使用压缩包替代

一、直接获取完整文件夹层级的方法

浏览器原生的<input type="file" directory multiple>存在局限性——只会返回包含文件的目录,空目录不会被单独提交,且IFormFile无法直接获取目录结构信息。要解决这个问题,需要借助前端JavaScript读取目录结构,再把层级数据和文件一起提交:

  1. 前端处理逻辑

    • 监听文件输入的change事件,通过webkitEntries(Chrome、Edge等现代浏览器支持)获取所有选中的目录和文件条目
    • 递归遍历目录条目,收集完整的目录层级(包括空文件夹)和文件信息
    • 将目录层级数据转为JSON字符串,和文件一起通过FormData提交到后端

    示例前端代码:

    document.querySelector('input[type="file"]').addEventListener('change', async (e) => {
      const formData = new FormData();
      const dirStructure = [];
    
      // 递归遍历目录
      async function traverseEntry(entry, parentPath = '') {
        const currentPath = parentPath ? `${parentPath}/${entry.name}` : entry.name;
        if (entry.isDirectory) {
          // 记录目录信息
          dirStructure.push({ path: currentPath, type: 'directory' });
          const reader = entry.createReader();
          const entries = await new Promise((resolve) => reader.readEntries(resolve));
          await Promise.all(entries.map(child => traverseEntry(child, currentPath)));
        } else if (entry.isFile) {
          // 记录文件信息并添加到FormData
          const file = await new Promise((resolve) => entry.file(resolve));
          formData.append('files', file, currentPath);
          dirStructure.push({ path: currentPath, type: 'file' });
        }
      }
    
      // 处理所有选中的条目
      for (const entry of e.target.webkitEntries) {
        await traverseEntry(entry);
      }
    
      // 将目录结构JSON添加到FormData
      formData.append('directoryStructure', JSON.stringify(dirStructure));
    
      // 提交到后端
      await fetch('/api/upload', {
        method: 'POST',
        body: formData
      });
    });
    
  2. 后端处理逻辑

    • 控制器中接收directoryStructure参数(JSON字符串)和文件列表
    • 解析JSON得到完整的目录层级,包含空文件夹
    • 将层级数据保存到数据库,同时处理上传的文件

    示例后端代码(ASP.NET Core):

    [HttpPost("api/upload")]
    public async Task<IActionResult> Upload([FromForm] IFormCollection form)
    {
        // 获取目录结构JSON并解析
        var dirStructureJson = form["directoryStructure"].FirstOrDefault();
        if (string.IsNullOrEmpty(dirStructureJson))
        {
            return BadRequest("未提供目录结构数据");
        }
        var directoryStructure = JsonSerializer.Deserialize<List<DirectoryEntry>>(dirStructureJson);
    
        // 保存目录层级到数据库
        foreach (var entry in directoryStructure.Where(e => e.Type == "directory"))
        {
            // 这里写保存到数据库的逻辑,比如创建Directory实体
            // dbContext.Directories.Add(new Directory { Path = entry.Path });
        }
        await dbContext.SaveChangesAsync();
    
        // 处理上传的文件(可选)
        var files = form.Files;
        foreach (var file in files)
        {
            // 处理文件保存逻辑
        }
    
        return Ok();
    }
    
    // 辅助类
    public class DirectoryEntry
    {
        public string Path { get; set; }
        public string Type { get; set; }
    }
    

    注意:这种方法依赖浏览器的webkitEntries API,兼容性上需要确认目标用户使用的浏览器是否支持(现代Chrome、Edge、Safari都支持,Firefox需要开启相关配置)。

二、要求用户上传压缩包的替代方案

如果前端兼容性要求较高,或者不想处理复杂的前端目录遍历逻辑,让用户上传压缩包(ZIP、RAR等)是更稳妥的方案:

  1. 前端提示:在页面上明确告知用户需要将目标文件夹压缩为ZIP包后上传,使用普通的<input type="file" accept=".zip">即可。

  2. 后端处理逻辑:

    • 接收上传的压缩包文件
    • 使用开源库(如ASP.NET Core中的SharpZipLib或System.IO.Compression)解压压缩包
    • 遍历解压后的目录结构,提取所有文件夹层级(包括空文件夹)
    • 将层级数据保存到数据库,同时可选择保留或删除解压后的文件

    示例后端解压代码(使用System.IO.Compression):

    [HttpPost("api/upload-zip")]
    public async Task<IActionResult> UploadZip(IFormFile zipFile)
    {
        if (zipFile == null || zipFile.Length == 0)
        {
            return BadRequest("请选择有效的压缩包文件");
        }
    
        var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
        Directory.CreateDirectory(tempDir);
    
        // 解压ZIP包
        using (var stream = new MemoryStream())
        {
            await zipFile.CopyToAsync(stream);
            stream.Position = 0;
            using (var archive = new ZipArchive(stream, ZipArchiveMode.Read))
            {
                archive.ExtractToDirectory(tempDir);
            }
        }
    
        // 遍历目录结构,收集层级
        var dirPaths = new List<string>();
        CollectDirectories(tempDir, dirPaths, Path.GetFileNameWithoutExtension(zipFile.FileName));
    
        // 保存到数据库
        foreach (var path in dirPaths)
        {
            // dbContext.Directories.Add(new Directory { Path = path });
        }
        await dbContext.SaveChangesAsync();
    
        // 清理临时目录(可选)
        Directory.Delete(tempDir, recursive: true);
    
        return Ok();
    }
    
    // 递归收集所有目录路径
    private void CollectDirectories(string rootDir, List<string> dirPaths, string basePath)
    {
        // 添加当前目录
        var relativePath = rootDir.Replace(Path.GetDirectoryName(rootDir), basePath).Replace("\\", "/");
        dirPaths.Add(relativePath);
    
        // 遍历子目录
        foreach (var subDir in Directory.GetDirectories(rootDir))
        {
            CollectDirectories(subDir, dirPaths, basePath);
        }
    }
    

这种方案兼容性好,不需要依赖浏览器特定API,缺点是用户需要多一步压缩操作,需要在页面上做好引导说明。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 00:42:32