不错呦!smile@林凯西,确保“准备文件”中的几个文件都有安装,S...您好,看了您这篇帖子觉得很有帮助。但是有个问题想请...我的修改过了怎么还被恶意注册呢 @jjjjiiii 用PJ快9年了,主要是A...PJ3啊,貌似很少有人用PJ了,现在不是WP就是z...@332347365,我当时接入时错误码没有-10...楼主,ChkValue值应为-103是什么意思呢?...大哥 你最近能看到我发的信息,请跟我联系,我有个制...
C#生成文件清单命令行工具(DeepSeek版)
编辑:dnawo 日期:2026-05-27

FileList 是 DeepSeek 编写的 Windows 控制台工具,用于快速生成指定文件夹下的文件清单,并将结果保存为 FileList.txt 文件。它支持两种输出模式:
■ 树形模式(默认) —— 以树状结构展示目录和文件,清晰直观。
■ 内容模式 —— 输出每个文件的完整路径和文本内容,便于批量查看或搜索。
支持按扩展名过滤(包含或排除),自动跳过非文本文件或超大文件,完美兼容命令行和双击运行两种使用场景。
命令行格式
引用内容FileList.exe [文件夹路径] [包含扩展名] [排除扩展名] [是否显示内容]
调用示例:
引用内容REM 查看帮助
FileList.exe /?
REM 列出当前目录树
FileList.exe
REM 列出 D:\Project 下所有 .cs 和 .csproj 文件(树形)
FileList.exe "D:\Project" ".cs|.csproj"
REM 排除 .tmp 和 .log 文件,并显示文件内容
FileList.exe "D:\Project" "" ".tmp|.log" true
FileList.exe /?
REM 列出当前目录树
FileList.exe
REM 列出 D:\Project 下所有 .cs 和 .csproj 文件(树形)
FileList.exe "D:\Project" ".cs|.csproj"
REM 排除 .tmp 和 .log 文件,并显示文件内容
FileList.exe "D:\Project" "" ".tmp|.log" true
程序代码
复制内容到剪贴板
程序代码
程序代码using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Text;
namespace FileList
{
class Program
{
/// <summary>
/// 常见文本文件扩展名集合,用于内容模式判断文件是否为文本类型。
/// 不区分大小写。
/// </summary>
static readonly HashSet<string> TextExtensions = new HashSet<string>(
StringComparer.OrdinalIgnoreCase)
{
".txt", ".md", ".markdown", ".html", ".htm", ".css", ".js", ".ts", ".tsx", ".jsx",
".json", ".xml", ".csv", ".config", ".cs", ".vb", ".cpp", ".c", ".h", ".hpp",
".py", ".php", ".asp", ".aspx", ".ini", ".log", ".bat", ".cmd", ".sh", ".yml",
".yaml", ".sql", ".svg", ".rb", ".java", ".pl", ".pm", ".go", ".rs", ".toml",
".lock", ".gitignore", ".gitattributes", ".editorconfig", ".env", ".proto"
};
/// <summary>
/// 程序入口。支持4个位置参数,按顺序为:
/// 1. 文件夹路径(空则当前目录)
/// 2. 包含扩展名(|分隔)
/// 3. 排除扩展名(|分隔)
/// 4. 是否显示文件内容(true/false)
/// </summary>
static int Main(string[] args)
{
// 检测当前运行环境是否为 cmd 或 powershell,决定最终是否等待按键
bool isCmdEnv = IsRunningFromCmd();
try
{
// 处理帮助请求
if (args.Length >= 1 && !string.IsNullOrEmpty(args[0]) &&
(args[0] == "/?" || args[0].Equals("-h", StringComparison.OrdinalIgnoreCase) ||
args[0].Equals("--help", StringComparison.OrdinalIgnoreCase)))
{
ShowHelp();
if (!isCmdEnv) WaitForKey();
return 0;
}
// 解析第一个参数:文件夹路径
string folderPath = null;
if (args.Length >= 1 && !string.IsNullOrEmpty(args[0]))
folderPath = args[0];
if (string.IsNullOrEmpty(folderPath))
folderPath = AppDomain.CurrentDomain.BaseDirectory; // 默认为 exe 所在目录
// 解析第二个参数:包含扩展名列表
string[] includeExts = null;
if (args.Length >= 2 && !string.IsNullOrEmpty(args[1]))
includeExts = NormalizeExtensions(
args[1].Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries));
// 解析第三个参数:排除扩展名列表
string[] excludeExts = null;
if (args.Length >= 3 && !string.IsNullOrEmpty(args[2]))
excludeExts = NormalizeExtensions(
args[2].Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries));
// 解析第四个参数:是否显示文件内容
bool showContent = false;
if (args.Length >= 4 && !string.IsNullOrEmpty(args[3]))
{
if (!bool.TryParse(args[3], out showContent))
{
Console.Error.WriteLine("Error: fourth parameter must be 'true' or 'false'.");
if (!isCmdEnv) WaitForKey();
return 1;
}
}
// 检查目录是否存在
if (!Directory.Exists(folderPath))
{
Console.Error.WriteLine($"Error: directory not found: {folderPath}");
if (!isCmdEnv) WaitForKey();
return 1;
}
// 检查输出路径是否是已存在的目录
string outputFile = Path.Combine(folderPath, "FileList.txt");
if (Directory.Exists(outputFile))
{
Console.Error.WriteLine($"Error: '{outputFile}' is a directory, cannot write.");
if (!isCmdEnv) WaitForKey();
return 1;
}
// 根据模式生成清单
if (showContent)
GenerateContentList(folderPath, outputFile, includeExts, excludeExts);
else
GenerateTreeList(folderPath, outputFile, includeExts, excludeExts);
Console.WriteLine("FileList.txt created successfully.");
if (!isCmdEnv) WaitForKey();
return 0;
}
catch (UnauthorizedAccessException ex)
{
Console.Error.WriteLine($"Error: access denied - {ex.Message}");
if (!isCmdEnv) WaitForKey();
return 1;
}
catch (IOException ex)
{
Console.Error.WriteLine($"Error: I/O error - {ex.Message}");
if (!isCmdEnv) WaitForKey();
return 1;
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error: {ex.Message}");
if (!isCmdEnv) WaitForKey();
return 1;
}
}
/// <summary>
/// 显示命令行帮助信息。
/// </summary>
static void ShowHelp()
{
Console.WriteLine("FileList - List files in a directory tree.");
Console.WriteLine("Usage:");
Console.WriteLine(" FileList.exe [folderPath] [includeExts] [excludeExts] [showContent]");
Console.WriteLine();
Console.WriteLine("Parameters:");
Console.WriteLine(" folderPath : Target directory. Default: current directory.");
Console.WriteLine(" includeExts : Use '|' separated extensions (e.g. .cs|.php).");
Console.WriteLine(" If provided, only these extensions are listed. (ignore excludeExts)");
Console.WriteLine(" excludeExts : Extensions to exclude (e.g. .tmp|.log).");
Console.WriteLine(" showContent : 'true' to output file paths and content, 'false' for tree view.");
Console.WriteLine();
Console.WriteLine("Notes:");
Console.WriteLine(" - In CMD, escape '|' with ^ or use quotes: \".cs|.php\"");
Console.WriteLine(" - Extensions are case-insensitive and may be specified with or without leading dot.");
}
/// <summary>
/// 标准化扩展名数组:去除空白,保证每个扩展名以点开头。
/// </summary>
static string[] NormalizeExtensions(string[] exts)
{
for (int i = 0; i < exts.Length; i++)
{
exts[i] = exts[i].Trim();
if (!exts[i].StartsWith("."))
exts[i] = "." + exts[i];
}
return exts;
}
/// <summary>
/// 通过 WMI 查询父进程,判断是否由 cmd 或 powershell 启动。
/// 如果判断失败则默认按独立运行处理(返回 false)。
/// </summary>
static bool IsRunningFromCmd()
{
try
{
using (var current = Process.GetCurrentProcess())
{
string query = $"Select ParentProcessId FROM Win32_Process Where ProcessId = {current.Id}";
using (var searcher = new ManagementObjectSearcher(query))
{
foreach (var obj in searcher.Get())
{
int parentId = Convert.ToInt32(obj["ParentProcessId"]);
var parent = Process.GetProcessById(parentId);
string name = parent.ProcessName.ToLower();
return name == "cmd" || name == "powershell";
}
}
}
}
catch
{
// 无法查询时按独立运行处理
}
return false;
}
/// <summary>
/// 独立运行时等待用户按键,防止窗口立即关闭。
/// </summary>
static void WaitForKey()
{
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
/// <summary>
/// 生成树形结构的文件列表(不含文件内容)。
/// </summary>
static void GenerateTreeList(string rootPath, string outputFile, string[] includeExts, string[] excludeExts)
{
// 构建根节点
var root = new TreeNode
{
Name = rootPath,
IsDirectory = true,
FullPath = rootPath
};
// 递归构建树,并过滤掉无匹配文件的空目录
BuildTree(root, includeExts, excludeExts);
var sb = new StringBuilder();
RenderTree(root, "", true, sb); // 渲染成字符串
File.WriteAllText(outputFile, sb.ToString(), Encoding.UTF8);
}
/// <summary>
/// 生成含文件内容的列表(扁平,无目录树)。
/// </summary>
static void GenerateContentList(string rootPath, string outputFile, string[] includeExts, string[] excludeExts)
{
// 获取所有符合过滤条件的文件,并按完整路径排序(不区分大小写)
var files = GetFilteredFiles(rootPath, includeExts, excludeExts);
files.Sort(StringComparer.OrdinalIgnoreCase);
using (var writer = new StreamWriter(outputFile, false, Encoding.UTF8))
{
foreach (var filePath in files)
{
writer.WriteLine(filePath + ":");
try
{
var fileInfo = new FileInfo(filePath);
const long maxSize = 1 * 1024 * 1024; // 1 MB
// 文件过大则跳过内容
if (fileInfo.Length > maxSize)
{
writer.WriteLine("[File size exceeds 1MB, content not displayed]");
}
// 非文本文件(根据扩展名)跳过内容
else if (!IsTextFile(filePath))
{
writer.WriteLine("[Non-text file, content not displayed]");
}
else
{
// 读取文件全部文本(UTF-8)
string content = File.ReadAllText(filePath, Encoding.UTF8);
writer.WriteLine(content);
}
}
catch (UnauthorizedAccessException)
{
throw; // 权限错误直接抛出,终止程序
}
catch (IOException)
{
throw;
}
writer.WriteLine("------------------------------------------------------------");
}
}
}
/// <summary>
/// 判断指定文件是否属于文本文件(基于扩展名)。
/// </summary>
static bool IsTextFile(string filePath)
{
string ext = Path.GetExtension(filePath);
return TextExtensions.Contains(ext);
}
/// <summary>
/// 递归构建树节点,并根据过滤条件判断是否保留该节点。
/// 返回 true 表示该节点(目录)下至少有一个匹配文件,应保留。
/// </summary>
static bool BuildTree(TreeNode node, string[] includeExts, string[] excludeExts)
{
// 如果是文件,直接判断是否符合过滤条件
if (!node.IsDirectory)
return IsFileIncluded(node.FullPath, includeExts, excludeExts);
var dirInfo = new DirectoryInfo(node.FullPath);
FileSystemInfo[] items;
try
{
items = dirInfo.GetFileSystemInfos(); // 包含隐藏文件和系统文件
}
catch (UnauthorizedAccessException)
{
throw; // 无权访问时直接抛出,由上层终止程序
}
var children = new List<TreeNode>();
foreach (var item in items)
{
var child = new TreeNode
{
Name = item.Name,
FullPath = item.FullName,
IsDirectory = (item.Attributes & FileAttributes.Directory) == FileAttributes.Directory
};
// 递归检查子项,只有包含匹配文件的子项才加入列表
bool hasFiles = BuildTree(child, includeExts, excludeExts);
if (hasFiles)
children.Add(child);
}
// 同级条目(文件+目录)按字母升序,不区分大小写
children.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase));
node.Children = children;
// 该目录是否保留:只要至少有一个子节点(文件或包含文件的子目录)
return children.Count > 0;
}
/// <summary>
/// 判断单个文件是否符合包含/排除扩展名规则。
/// 如果指定了包含列表,则文件扩展名必须在列表中;
/// 否则,如果指定了排除列表,文件扩展名不能在其中;
/// 都未指定则匹配所有文件。
/// </summary>
static bool IsFileIncluded(string filePath, string[] includeExts, string[] excludeExts)
{
string ext = Path.GetExtension(filePath);
if (includeExts != null && includeExts.Length > 0)
return includeExts.Any(e => string.Equals(e, ext, StringComparison.OrdinalIgnoreCase));
if (excludeExts != null && excludeExts.Length > 0)
return !excludeExts.Any(e => string.Equals(e, ext, StringComparison.OrdinalIgnoreCase));
return true;
}
/// <summary>
/// 递归获取指定目录下所有符合过滤条件的文件完整路径(扁平列表)。
/// </summary>
static List<string> GetFilteredFiles(string rootPath, string[] includeExts, string[] excludeExts)
{
var result = new List<string>();
var dirInfo = new DirectoryInfo(rootPath);
FileSystemInfo[] items;
try
{
items = dirInfo.GetFileSystemInfos();
}
catch (UnauthorizedAccessException)
{
throw;
}
foreach (var item in items)
{
if ((item.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
{
try
{
// 递归添加子目录中的文件
result.AddRange(GetFilteredFiles(item.FullName, includeExts, excludeExts));
}
catch (UnauthorizedAccessException)
{
throw;
}
}
else
{
if (IsFileIncluded(item.FullName, includeExts, excludeExts))
result.Add(item.FullName);
}
}
return result;
}
/// <summary>
/// 递归渲染树形结构到 StringBuilder。
/// </summary>
/// <param name="node">当前节点</param>
/// <param name="indent">当前行的缩进前缀</param>
/// <param name="isLast">当前节点是否为同级最后一个(决定使用 └── 还是 ├──)</param>
/// <param name="sb">输出缓冲</param>
static void RenderTree(TreeNode node, string indent, bool isLast, StringBuilder sb)
{
// 根节点特殊处理:直接输出完整路径,不加树形符号
if (node.IsDirectory && node.Name == node.FullPath)
{
sb.AppendLine(node.Name);
}
else
{
sb.Append(indent);
sb.Append(isLast ? "└──" : "├──");
sb.AppendLine(node.Name);
}
// 处理子节点
if (node.IsDirectory && node.Children != null)
{
for (int i = 0; i < node.Children.Count; i++)
{
bool lastChild = (i == node.Children.Count - 1);
// 根节点的直接子节点缩进为空(不产生多余空格)
string childIndent;
if (node.Name == node.FullPath)
{
childIndent = ""; // 根下一级无额外缩进
}
else
{
childIndent = indent + (isLast ? " " : "│ ");
}
RenderTree(node.Children[i], childIndent, lastChild, sb);
}
}
}
/// <summary>
/// 树节点,表示文件或目录。
/// </summary>
class TreeNode
{
public string Name; // 文件/目录名
public string FullPath; // 完整路径
public bool IsDirectory; // 是否为目录
public List<TreeNode> Children; // 子节点列表(仅目录有效)
}
}
}
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Text;
namespace FileList
{
class Program
{
/// <summary>
/// 常见文本文件扩展名集合,用于内容模式判断文件是否为文本类型。
/// 不区分大小写。
/// </summary>
static readonly HashSet<string> TextExtensions = new HashSet<string>(
StringComparer.OrdinalIgnoreCase)
{
".txt", ".md", ".markdown", ".html", ".htm", ".css", ".js", ".ts", ".tsx", ".jsx",
".json", ".xml", ".csv", ".config", ".cs", ".vb", ".cpp", ".c", ".h", ".hpp",
".py", ".php", ".asp", ".aspx", ".ini", ".log", ".bat", ".cmd", ".sh", ".yml",
".yaml", ".sql", ".svg", ".rb", ".java", ".pl", ".pm", ".go", ".rs", ".toml",
".lock", ".gitignore", ".gitattributes", ".editorconfig", ".env", ".proto"
};
/// <summary>
/// 程序入口。支持4个位置参数,按顺序为:
/// 1. 文件夹路径(空则当前目录)
/// 2. 包含扩展名(|分隔)
/// 3. 排除扩展名(|分隔)
/// 4. 是否显示文件内容(true/false)
/// </summary>
static int Main(string[] args)
{
// 检测当前运行环境是否为 cmd 或 powershell,决定最终是否等待按键
bool isCmdEnv = IsRunningFromCmd();
try
{
// 处理帮助请求
if (args.Length >= 1 && !string.IsNullOrEmpty(args[0]) &&
(args[0] == "/?" || args[0].Equals("-h", StringComparison.OrdinalIgnoreCase) ||
args[0].Equals("--help", StringComparison.OrdinalIgnoreCase)))
{
ShowHelp();
if (!isCmdEnv) WaitForKey();
return 0;
}
// 解析第一个参数:文件夹路径
string folderPath = null;
if (args.Length >= 1 && !string.IsNullOrEmpty(args[0]))
folderPath = args[0];
if (string.IsNullOrEmpty(folderPath))
folderPath = AppDomain.CurrentDomain.BaseDirectory; // 默认为 exe 所在目录
// 解析第二个参数:包含扩展名列表
string[] includeExts = null;
if (args.Length >= 2 && !string.IsNullOrEmpty(args[1]))
includeExts = NormalizeExtensions(
args[1].Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries));
// 解析第三个参数:排除扩展名列表
string[] excludeExts = null;
if (args.Length >= 3 && !string.IsNullOrEmpty(args[2]))
excludeExts = NormalizeExtensions(
args[2].Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries));
// 解析第四个参数:是否显示文件内容
bool showContent = false;
if (args.Length >= 4 && !string.IsNullOrEmpty(args[3]))
{
if (!bool.TryParse(args[3], out showContent))
{
Console.Error.WriteLine("Error: fourth parameter must be 'true' or 'false'.");
if (!isCmdEnv) WaitForKey();
return 1;
}
}
// 检查目录是否存在
if (!Directory.Exists(folderPath))
{
Console.Error.WriteLine($"Error: directory not found: {folderPath}");
if (!isCmdEnv) WaitForKey();
return 1;
}
// 检查输出路径是否是已存在的目录
string outputFile = Path.Combine(folderPath, "FileList.txt");
if (Directory.Exists(outputFile))
{
Console.Error.WriteLine($"Error: '{outputFile}' is a directory, cannot write.");
if (!isCmdEnv) WaitForKey();
return 1;
}
// 根据模式生成清单
if (showContent)
GenerateContentList(folderPath, outputFile, includeExts, excludeExts);
else
GenerateTreeList(folderPath, outputFile, includeExts, excludeExts);
Console.WriteLine("FileList.txt created successfully.");
if (!isCmdEnv) WaitForKey();
return 0;
}
catch (UnauthorizedAccessException ex)
{
Console.Error.WriteLine($"Error: access denied - {ex.Message}");
if (!isCmdEnv) WaitForKey();
return 1;
}
catch (IOException ex)
{
Console.Error.WriteLine($"Error: I/O error - {ex.Message}");
if (!isCmdEnv) WaitForKey();
return 1;
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error: {ex.Message}");
if (!isCmdEnv) WaitForKey();
return 1;
}
}
/// <summary>
/// 显示命令行帮助信息。
/// </summary>
static void ShowHelp()
{
Console.WriteLine("FileList - List files in a directory tree.");
Console.WriteLine("Usage:");
Console.WriteLine(" FileList.exe [folderPath] [includeExts] [excludeExts] [showContent]");
Console.WriteLine();
Console.WriteLine("Parameters:");
Console.WriteLine(" folderPath : Target directory. Default: current directory.");
Console.WriteLine(" includeExts : Use '|' separated extensions (e.g. .cs|.php).");
Console.WriteLine(" If provided, only these extensions are listed. (ignore excludeExts)");
Console.WriteLine(" excludeExts : Extensions to exclude (e.g. .tmp|.log).");
Console.WriteLine(" showContent : 'true' to output file paths and content, 'false' for tree view.");
Console.WriteLine();
Console.WriteLine("Notes:");
Console.WriteLine(" - In CMD, escape '|' with ^ or use quotes: \".cs|.php\"");
Console.WriteLine(" - Extensions are case-insensitive and may be specified with or without leading dot.");
}
/// <summary>
/// 标准化扩展名数组:去除空白,保证每个扩展名以点开头。
/// </summary>
static string[] NormalizeExtensions(string[] exts)
{
for (int i = 0; i < exts.Length; i++)
{
exts[i] = exts[i].Trim();
if (!exts[i].StartsWith("."))
exts[i] = "." + exts[i];
}
return exts;
}
/// <summary>
/// 通过 WMI 查询父进程,判断是否由 cmd 或 powershell 启动。
/// 如果判断失败则默认按独立运行处理(返回 false)。
/// </summary>
static bool IsRunningFromCmd()
{
try
{
using (var current = Process.GetCurrentProcess())
{
string query = $"Select ParentProcessId FROM Win32_Process Where ProcessId = {current.Id}";
using (var searcher = new ManagementObjectSearcher(query))
{
foreach (var obj in searcher.Get())
{
int parentId = Convert.ToInt32(obj["ParentProcessId"]);
var parent = Process.GetProcessById(parentId);
string name = parent.ProcessName.ToLower();
return name == "cmd" || name == "powershell";
}
}
}
}
catch
{
// 无法查询时按独立运行处理
}
return false;
}
/// <summary>
/// 独立运行时等待用户按键,防止窗口立即关闭。
/// </summary>
static void WaitForKey()
{
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
/// <summary>
/// 生成树形结构的文件列表(不含文件内容)。
/// </summary>
static void GenerateTreeList(string rootPath, string outputFile, string[] includeExts, string[] excludeExts)
{
// 构建根节点
var root = new TreeNode
{
Name = rootPath,
IsDirectory = true,
FullPath = rootPath
};
// 递归构建树,并过滤掉无匹配文件的空目录
BuildTree(root, includeExts, excludeExts);
var sb = new StringBuilder();
RenderTree(root, "", true, sb); // 渲染成字符串
File.WriteAllText(outputFile, sb.ToString(), Encoding.UTF8);
}
/// <summary>
/// 生成含文件内容的列表(扁平,无目录树)。
/// </summary>
static void GenerateContentList(string rootPath, string outputFile, string[] includeExts, string[] excludeExts)
{
// 获取所有符合过滤条件的文件,并按完整路径排序(不区分大小写)
var files = GetFilteredFiles(rootPath, includeExts, excludeExts);
files.Sort(StringComparer.OrdinalIgnoreCase);
using (var writer = new StreamWriter(outputFile, false, Encoding.UTF8))
{
foreach (var filePath in files)
{
writer.WriteLine(filePath + ":");
try
{
var fileInfo = new FileInfo(filePath);
const long maxSize = 1 * 1024 * 1024; // 1 MB
// 文件过大则跳过内容
if (fileInfo.Length > maxSize)
{
writer.WriteLine("[File size exceeds 1MB, content not displayed]");
}
// 非文本文件(根据扩展名)跳过内容
else if (!IsTextFile(filePath))
{
writer.WriteLine("[Non-text file, content not displayed]");
}
else
{
// 读取文件全部文本(UTF-8)
string content = File.ReadAllText(filePath, Encoding.UTF8);
writer.WriteLine(content);
}
}
catch (UnauthorizedAccessException)
{
throw; // 权限错误直接抛出,终止程序
}
catch (IOException)
{
throw;
}
writer.WriteLine("------------------------------------------------------------");
}
}
}
/// <summary>
/// 判断指定文件是否属于文本文件(基于扩展名)。
/// </summary>
static bool IsTextFile(string filePath)
{
string ext = Path.GetExtension(filePath);
return TextExtensions.Contains(ext);
}
/// <summary>
/// 递归构建树节点,并根据过滤条件判断是否保留该节点。
/// 返回 true 表示该节点(目录)下至少有一个匹配文件,应保留。
/// </summary>
static bool BuildTree(TreeNode node, string[] includeExts, string[] excludeExts)
{
// 如果是文件,直接判断是否符合过滤条件
if (!node.IsDirectory)
return IsFileIncluded(node.FullPath, includeExts, excludeExts);
var dirInfo = new DirectoryInfo(node.FullPath);
FileSystemInfo[] items;
try
{
items = dirInfo.GetFileSystemInfos(); // 包含隐藏文件和系统文件
}
catch (UnauthorizedAccessException)
{
throw; // 无权访问时直接抛出,由上层终止程序
}
var children = new List<TreeNode>();
foreach (var item in items)
{
var child = new TreeNode
{
Name = item.Name,
FullPath = item.FullName,
IsDirectory = (item.Attributes & FileAttributes.Directory) == FileAttributes.Directory
};
// 递归检查子项,只有包含匹配文件的子项才加入列表
bool hasFiles = BuildTree(child, includeExts, excludeExts);
if (hasFiles)
children.Add(child);
}
// 同级条目(文件+目录)按字母升序,不区分大小写
children.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase));
node.Children = children;
// 该目录是否保留:只要至少有一个子节点(文件或包含文件的子目录)
return children.Count > 0;
}
/// <summary>
/// 判断单个文件是否符合包含/排除扩展名规则。
/// 如果指定了包含列表,则文件扩展名必须在列表中;
/// 否则,如果指定了排除列表,文件扩展名不能在其中;
/// 都未指定则匹配所有文件。
/// </summary>
static bool IsFileIncluded(string filePath, string[] includeExts, string[] excludeExts)
{
string ext = Path.GetExtension(filePath);
if (includeExts != null && includeExts.Length > 0)
return includeExts.Any(e => string.Equals(e, ext, StringComparison.OrdinalIgnoreCase));
if (excludeExts != null && excludeExts.Length > 0)
return !excludeExts.Any(e => string.Equals(e, ext, StringComparison.OrdinalIgnoreCase));
return true;
}
/// <summary>
/// 递归获取指定目录下所有符合过滤条件的文件完整路径(扁平列表)。
/// </summary>
static List<string> GetFilteredFiles(string rootPath, string[] includeExts, string[] excludeExts)
{
var result = new List<string>();
var dirInfo = new DirectoryInfo(rootPath);
FileSystemInfo[] items;
try
{
items = dirInfo.GetFileSystemInfos();
}
catch (UnauthorizedAccessException)
{
throw;
}
foreach (var item in items)
{
if ((item.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
{
try
{
// 递归添加子目录中的文件
result.AddRange(GetFilteredFiles(item.FullName, includeExts, excludeExts));
}
catch (UnauthorizedAccessException)
{
throw;
}
}
else
{
if (IsFileIncluded(item.FullName, includeExts, excludeExts))
result.Add(item.FullName);
}
}
return result;
}
/// <summary>
/// 递归渲染树形结构到 StringBuilder。
/// </summary>
/// <param name="node">当前节点</param>
/// <param name="indent">当前行的缩进前缀</param>
/// <param name="isLast">当前节点是否为同级最后一个(决定使用 └── 还是 ├──)</param>
/// <param name="sb">输出缓冲</param>
static void RenderTree(TreeNode node, string indent, bool isLast, StringBuilder sb)
{
// 根节点特殊处理:直接输出完整路径,不加树形符号
if (node.IsDirectory && node.Name == node.FullPath)
{
sb.AppendLine(node.Name);
}
else
{
sb.Append(indent);
sb.Append(isLast ? "└──" : "├──");
sb.AppendLine(node.Name);
}
// 处理子节点
if (node.IsDirectory && node.Children != null)
{
for (int i = 0; i < node.Children.Count; i++)
{
bool lastChild = (i == node.Children.Count - 1);
// 根节点的直接子节点缩进为空(不产生多余空格)
string childIndent;
if (node.Name == node.FullPath)
{
childIndent = ""; // 根下一级无额外缩进
}
else
{
childIndent = indent + (isLast ? " " : "│ ");
}
RenderTree(node.Children[i], childIndent, lastChild, sb);
}
}
}
/// <summary>
/// 树节点,表示文件或目录。
/// </summary>
class TreeNode
{
public string Name; // 文件/目录名
public string FullPath; // 完整路径
public bool IsDirectory; // 是否为目录
public List<TreeNode> Children; // 子节点列表(仅目录有效)
}
}
}
运行效果:
引用内容E:\Web
├──AIAPP.md
├──index.html
├──package-lock.json
├──package.json
├──postcss.config.js
├──src
│ ├──app.tsx
│ ├──components
│ │ ├──ImagePreview.tsx
│ │ └──Layout.tsx
│ ├──context
│ │ └──AuthContext.tsx
│ ├──index.css
│ ├──index.tsx
│ ├──lib
│ │ ├──captcha.ts
│ │ ├──image-compressor.ts
│ │ ├──supabase.ts
│ │ └──utils.ts
│ ├──pages
│ │ ├──AdminListPage.tsx
│ │ ├──CustomerListPage tbody.txt
│ │ ├──CustomerListPage.tsx
│ │ ├──CustomerListPage.tsx.bak
│ │ ├──DashboardPage.tsx
│ │ ├──InitPage.tsx
│ │ ├──ItemListPage.tsx
│ │ ├──LoginPage.tsx
│ │ └──PublicCustomerRepairsPage.tsx
│ └──types
│ └──database.ts
├──supabase
│ ├──migration
│ │ ├──001_create_customer_share_tokens.sql
│ │ ├──002_truncate_tables.sql
│ │ ├──003_fix_latitude_longitude_type.sql
│ │ ├──004_add_admin_login_security.sql
│ │ ├──005_add_administrator_to_repair_items.sql
│ │ ├──006_change_images_to_single_url.sql
│ │ ├──007_update_admin_roles_to_three_levels.sql
│ │ ├──add_admin_login_fields.sql
│ │ └──fix_last_login_ip_length.sql
│ └──tables
│ └──schema.sql
├──tailwind.config.js
├──tsconfig.json
├──tsconfig.node.json
└──vite.config.ts
├──AIAPP.md
├──index.html
├──package-lock.json
├──package.json
├──postcss.config.js
├──src
│ ├──app.tsx
│ ├──components
│ │ ├──ImagePreview.tsx
│ │ └──Layout.tsx
│ ├──context
│ │ └──AuthContext.tsx
│ ├──index.css
│ ├──index.tsx
│ ├──lib
│ │ ├──captcha.ts
│ │ ├──image-compressor.ts
│ │ ├──supabase.ts
│ │ └──utils.ts
│ ├──pages
│ │ ├──AdminListPage.tsx
│ │ ├──CustomerListPage tbody.txt
│ │ ├──CustomerListPage.tsx
│ │ ├──CustomerListPage.tsx.bak
│ │ ├──DashboardPage.tsx
│ │ ├──InitPage.tsx
│ │ ├──ItemListPage.tsx
│ │ ├──LoginPage.tsx
│ │ └──PublicCustomerRepairsPage.tsx
│ └──types
│ └──database.ts
├──supabase
│ ├──migration
│ │ ├──001_create_customer_share_tokens.sql
│ │ ├──002_truncate_tables.sql
│ │ ├──003_fix_latitude_longitude_type.sql
│ │ ├──004_add_admin_login_security.sql
│ │ ├──005_add_administrator_to_repair_items.sql
│ │ ├──006_change_images_to_single_url.sql
│ │ ├──007_update_admin_roles_to_three_levels.sql
│ │ ├──add_admin_login_fields.sql
│ │ └──fix_last_login_ip_length.sql
│ └──tables
│ └──schema.sql
├──tailwind.config.js
├──tsconfig.json
├──tsconfig.node.json
└──vite.config.ts
相关链接
[1].FileList开发文档:https://www.mzwu.com/doc/FileList.pdf
上一篇: Edge浏览器控制台生成本地文件示例
下一篇: IIS 10 HTTPS 配置教程:用 Certify The Web 自动申请 SSL 证书
文章来自: 本站原创
Tags:
最新日志:
评论: 0 | 引用: 0 | 查看次数: 158
发表评论
请登录后再发表评论!



