应通过读取文件头魔数判断类型,而非依赖扩展名;跨卷移动需先复制后删除;并发创建目录须用幂等的Directory.CreateDirectory;xml/jsON路由前需轻量解析预检。

怎么用 C# 判断文件类型而不是只看扩展名
靠 Path.GetExtension() 分发文件极不可靠——用户随便改个 .txt 为 .jpg,你的路由就崩了。真实场景里必须检查文件内容(魔数 / signature)。C# 没有内置“识别所有文件类型”的函数,得自己读头几十字节比对。
实操建议:
- 对常见格式(pdf、PNG、JPEG、ZIP、XML、json)建立魔数表,比如 PDF 固定以
%PDF-开头(注意带百分号),PNG 是x89PNGrnx1an - 用
Filestream打开文件,Read前 32 字节足矣,别全读——大文件下性能差还浪费内存 - 注意编码:二进制比对必须用
byte[],别转成字符串再比较,UTF-8 解码可能破坏原始字节 - 遇到无签名的纯文本(.csv、.log、.conf),可 fallback 到
Path.GetExtension()+ 白名单校验,但要加日志告警
File.Move() 在跨卷时失败怎么办
直接调用 File.Move() 分发文件,如果目标目录在另一磁盘(比如 D:routespdf 和 E:inbox),会抛 IOException:“The source and destination path must have the same root.”
这不是 bug,是 windows API 限制:MoveFileEx 要求同卷。绕过方法只有“复制 + 删除”,但得手动处理原子性与异常回滚。
实操建议:
- 先用
Path.GetPathRoot(source)和Path.GetPathRoot(destination)判断是否同卷 - 不同卷时,用
File.copy(source, dest, true)+File.delete(source),但必须包裹在try/catch中;若Delete失败,要记录残留文件路径供人工清理 - 别用
File.Replace()——它只适用于同卷且需保留旧版本备份,和路由无关 - 考虑加个重试逻辑(最多 2 次),网络映射盘偶尔因瞬时断连失败
并发处理多个文件时如何避免目录冲突
多线程或 Task 并行处理一批文件时,Directory.CreateDirectory() 可能被多次调用,导致 IOException:“Cannot create a file when that file already exists.”
这不是竞态条件,而是 CreateDirectory 自身不保证幂等——它只在目录不存在时创建,存在则抛异常(.net 5+ 已修复为静默忽略,但旧版仍需兼容)。
实操建议:
- 统一用
Directory.CreateDirectory(targetDir)—— 它返回DirectoryInfo,且从 .NET Core 3.0 起已保证幂等(旧框架如 .NET Framework 4.7.2 需自行 try/catchDirectory.Exists) - 不要自己写
if (!Directory.Exists()) Directory.Create(),竞态窗口依然存在 - 目标路径含动态部分(如按日期分目录
./pdf/2024-06-15/)时,确保父目录(./pdf/)提前建好,减少并发点 - 日志中记录实际创建成功的目录,方便排查“为什么某天目录没生成”
怎么安全地把 XML 或 JSON 文件路由到结构化处理管道
单纯按扩展名分发 .xml 或 .json 文件很危险:文件可能是空的、编码错误、或根本不是合法格式(比如 XML 文件开头缺 ,JSON 含 bom 或注释)。直接扔给 XDocument.Load() 或 JsonSerializer.Deserialize() 会崩溃。
路由层不该承担解析责任,但必须做轻量级预检,否则错误会漏到下游,难以定位源头。
实操建议:
- XML:读前 256 字节,用
XmlReader.Create(stream, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore })尝试 Read() 一次,捕获XmlException即判定无效 - JSON:用
JsonDocument.Parse(jsonBytes, new JsonDocumentOptions { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip }),不依赖第三方库也能快速验证 - 预检失败的文件,统一移到
./quarantine/invalid/并附带错误信息文件(如report.txt写明“JSON parse failed at offset 1024”) - 别在路由逻辑里做字段级校验(比如“必须含
invoiceId字段”)——那是业务处理器的事,路由只管“是不是能打开”
魔数判断和跨卷移动是硬门槛,很多人卡在这两步就退回用扩展名硬分。其实只要守住“先读头、再判断卷、最后建目录”这个顺序,95% 的文件路由场景都能稳住。剩下那 5%,通常是加密文件或自定义二进制格式——得和上游约好签名规则,不能靠猜。