怎么用 C# 判断文件类型而不是只看扩展名
靠
Path.GetExtension()分发文件极不可靠——用户随便改个 .txt 为 .jpg,你的路由就崩了。真实场景里必须检查文件内容(魔数 / signature)。C# 没有内置“识别所有文件类型”的函数,得自己读头几十字节比对。
实操建议:
对常见格式(PDF、PNG、JPEG、ZIP、XML、JSON)建立魔数表,比如 PDF 固定以%PDF-开头(注意带百分号),PNG 是
\x89PNG\r\n\x1a\n用
FileStream打开文件,
Read前 32 字节足矣,别全读——大文件下性能差还浪费内存 注意编码:二进制比对必须用
byte[],别转成字符串再比较,UTF-8 解码可能破坏原始字节 遇到无签名的纯文本(.csv、.log、.conf),可 fallback 到
Path.GetExtension()+ 白名单校验,但要加日志告警
File.Move() 在跨卷时失败怎么办
直接调用
File.Move()分发文件,如果目标目录在另一磁盘(比如 D:\routes\pdf 和 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/catch
Directory.Exists) 不要自己写
if (!Directory.Exists()) Directory.Create(),竞态窗口依然存在 目标路径含动态部分(如按日期分目录
./pdf/2024-06-15/)时,确保父目录(
./pdf/)提前建好,减少并发点 日志中记录实际创建成功的目录,方便排查“为什么某天目录没生成”
怎么安全地把 XML 或 JSON 文件路由到结构化处理管道
单纯按扩展名分发 .xml 或 .json 文件很危险:文件可能是空的、编码错误、或根本不是合法格式(比如 XML 文件开头缺
<?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%,通常是加密文件或自定义二进制格式——得和上游约好签名规则,不能靠猜。
