在 Excel Power Query 中处理文件夹读取场景时,很多人会遇到一个 "顽固" 问题:明明用了try...otherwise包裹Folder.Files函数,可当目标文件夹路径不存在时,依然会触发DataSource.NotFound硬报错,otherwise分支完全不生效。
在出现这个问题后,我同时向ChatGPT、Gemini、Deepseek和豆包提问解决方式,每个AI会话轮数都超过20次,但没有一个解决了问题,最终发现还是不能偷懒,自力更生,研究出了解决方法。
特别有意思的是ChatGPT,每次都信誓旦旦,一定可以解决问题/完美方案/终极方案,可惜次次令人失望。看来AI在某些方面,还是有很大进步空间。


本文将拆解问题根源,并分享一种 "从确定存在的上级目录入手" 的完美解决方案,让文件夹不存在时优雅返回空表,存在时正常读取数据。
一、问题:为什么 try 包裹 Folder.Files 依然报错?
先看一个典型的 "踩坑" 代码(也是很多人的第一反应):
powerquery-m
let
目标路径 = "E:\测试\不存在的文件夹\",
// 试图用try捕获错误,却无效
源 = try Folder.Files(目标路径) otherwise #table({}, {}),
结果 = 源
in
结果
执行后依然会弹出:DataSource.NotFound: File or Folder: 找不到文件夹,这是因为:
- Excel Power Query 的错误分为两层:M 表达式错误(如语法错、空值引用)可被
try捕获;数据源连接错误(如路径不存在、权限不足)发生在 M 表达式执行之前,属于 "连接层硬错误",try完全无法拦截。 Folder.Files访问不存在的路径时,触发的是后者 ——Power Query 会先尝试建立数据源连接,连接失败直接报错,根本不会执行try...otherwise逻辑。
二、核心思路:从 "确定存在的上级目录" 验证目标路径
既然直接访问未知路径会报错,那换个思路:先读取 "确定存在" 的上级目录,再从上级目录的文件 / 文件夹列表中,判断目标路径是否存在。
核心逻辑:
- 找到一个 "100% 存在" 的上级根目录(比如业务场景中固定的办公文件根目录);
- 读取该根目录下的所有文件夹路径列表;
- 检查目标文件夹路径是否包含在列表中:
- 存在:调用自定义函数执行正常的文件读取逻辑;
- 不存在:直接返回预设的空表,全程不触发硬报错。空表与有数据的表,在Power Pivot中可以完美融合。


三、完美解决方案
以下是经过验证的完整代码,适配所有 Excel Power Query 版本,可根据注释,根据自己的需要修改套用:
powerquery-m
let
// 步骤1:定义确定存在的根目录(关键!必须是100%存在的路径)
根目录 = Text.BeforeDelimiter(文件位置, "11客户"), // 业务场景中固定的上级目录
// 步骤2:定义目标文件夹路径(你要读取的宝贝报表目录)
宝贝报表目录 = 根目录 & "04万相台无界\宝贝报表\",
// 步骤3:读取根目录下的所有文件/文件夹(根目录存在,不会报错)
文件列表 = Folder.Files(根目录),
// 步骤4:封装正常的数据处理逻辑为自定义函数
文件转换fx = (X) =>
let
// 正常读取目标文件夹文件(此时路径已验证存在,不会报错)
源 = Folder.Files(X),
// 原有数据处理逻辑(按需修改,保持和业务一致)
添加filepath = Table.AddColumn(源, "filepath", each [Folder Path]&[Name]),
插入月份文本 = Table.AddColumn(添加filepath, "月", each Text.BetweenDelimiters([Name], "_", ".csv") & "-01", type date),
更改月类型 = Table.TransformColumnTypes(插入月份文本,{{"月", type date}}),
DateEndOfMonth = Table.TransformColumns(更改月类型, {"月", each Date.EndOfMonth(_), type date}),
筛选的行 = Table.SelectRows(DateEndOfMonth, each ([月] >= 数据日期)),
保留filepath = Table.SelectColumns(筛选的行,{"filepath"}),
二进制转换 = Table.TransformColumns(保留filepath, {"filepath", each 宝贝报表fx(_)}),
展开 = Table.ExpandTableColumn(二进制转换, "filepath", {"日期", "计划ID", "计划名字", "主体ID", "主体名称", "花费"}, {"日期", "计划ID", "计划名字", "主体ID", "主体名称", "花费"})
in
展开,
// 步骤5:定义预设空表(列名和类型与业务一致)
空表 = #table(
{"日期", "计划ID", "计划名字", "主体ID", "主体名称", "花费"}, // 列名匹配业务需求
{} // 空行,保证无数据
),
// 步骤6:分支判断(核心!验证目标路径是否存在)
分支 = if List.Contains(文件列表[Folder Path], 宝贝报表目录) then
文件转换fx(宝贝报表目录) // 路径存在,执行正常逻辑
else
空表, // 路径不存在,返回空表
// 步骤7:统一设置列类型(保证输出格式一致)
更改类型 = Table.TransformColumnTypes(分支,{{"日期", type date}, {"花费", type number}})
in
更改类型
四、代码关键解析
1. 根目录的选择(重中之重)
根目录必须是业务场景中 100% 存在的路径(比如示例中的Text.BeforeDelimiter(文件位置, "11客户")),这是整个方案的基础 —— 只有根目录存在,Folder.Files(根目录)才不会报错,才能拿到后续判断的依据。
2. 自定义函数封装业务逻辑
将正常的文件读取、数据处理逻辑封装为文件转换fx函数,好处是:
- 代码结构清晰,分离 "路径验证" 和 "数据处理" 逻辑;
- 仅在路径验证通过后才调用函数,避免无效执行。
3. 路径存在性判断
List.Contains(文件列表[Folder Path], 宝贝报表目录)是核心判断:
文件列表[Folder Path]:根目录下所有子文件夹的完整路径列表;List.Contains:检查目标路径是否在列表中,返回true/false(注意:目标路径要以字符"\"结尾);- 全程不直接访问目标路径,而是通过 "列表查询" 验证,从根源规避硬报错。
4. 空表的定义
用#table定义空表,保证列名(日期、计划 ID 等)和业务输出完全一致,避免后续引用该查询时因列名 / 结构不一致报错。
五、方案优势
- 彻底避错:全程仅访问 "确定存在的根目录",目标路径不存在时不调用
Folder.Files,无任何数据源硬报错; - 逻辑完整:路径存在时执行原有业务逻辑,数据读取、处理不受影响;
- 适配性强:仅使用 Excel Power Query 原生函数,无 Power BI 专属函数,全版本兼容;
- 易维护:自定义函数封装业务逻辑,后续修改数据处理规则只需改函数内部,无需动验证逻辑。
六、总结
Excel Power Query 中Folder.Files的硬报错问题,核心症结是 "直接访问未知路径触发连接层错误",而解决的关键是 "绕开直接访问"—— 从确定存在的上级目录入手,通过列表查询验证目标路径,再分支执行逻辑。
这个方案的核心思想可以总结为:先验证,后执行;验证用已知路径,执行业务逻辑。无论是 "万相台无界" 文件夹不存在,还是 "宝贝报表" 子文件夹不存在,都能优雅返回空表;路径存在时,又能正常读取和处理数据,完美解决了 "报错" 和 "业务逻辑" 的双重需求。