比如有这样的一个需求,旧的一个业务系统通常将产出的文件输出到同一个指定的目录下的不同子目录,输出的文件类型有 XML,EXCEL, TXT 这些不同后缀的文件。现在需要在 SSIS 中将它们分类整理到不同的文件夹下,比如 XML 的全部拷贝到 A 文件夹, EXCEL 全部拷贝到 B 文件夹。
其实文件操作在 C# 中直接编程可能更好解决,不需要特别多的代码就可以完成这个拷贝的任务。SSIS 中比 C# 编程优势的地方可以体现在 SSIS 可以直接解析文件并作为数据源输出到指定的目的地中,比如平面文件,EXCEL 或者数据库等,包括数据拆分,异常捕获,错误数据处理等,涉及到流程控制部分还是会优先选择 SSIS 。
但是在这里通过这样的一个 SSIS 小例子来掌握一下在 SSIS 中如何获取指定目录下的所有文件,以及如何拷贝文件的操作。并且,在这个例子中还介绍了如何在 Control Flow 中在没有先决条件的情况下主动给自己创造一个先决条件等小技巧。
具体问题具体对待,这个例子在实际使用中并不合理,再次强调! 但是可以从这个例子中选择部分处理技巧或者处理方式延用到实际项目中,这个还是完全可以的。
新建一个包并创建以下变量 -
DestinationDirectory - 输出的目的地文件夹,将根据文件后缀决定。比如如果文件后缀是 .txt 那么就输出到 TXTFileDirectory 变量指定的文件夹下;如果文件是 .xml 类型,那么就输出到 XMLFileDirectory 变量指定的文件夹下。
它的 Expression 表达式 -
FINDSTRING( @[User::FileName],".txt",1)!=0? @[User::TXTFileDirectory]:(FINDSTRING( @[User::FileName],".xml",1)!=0? @[User::XMLFileDirectory] : "")
Directory - 源文件夹
FileExtensionFilter - 为了演示这个例子,我们假设要循环获取 Directory 目录下的所有文件 *.*,然后在这些文件中选择 .txt 和 .xml 后缀的文件拷贝到指定目录下。
(技巧一 - 如何获取指定目录下所有文件名称)
FileName - 文件的名称
FileNames - 获取所有的文件名称集合,这个集合注意是 Object 类型的 - 使用 SSIS 中的 Foreach Loop Container 是可以遍历数组的。
(技巧二 - 在循环遍历的数组类型在 SSIS 用什么数据类型表示 - OBJECT)
IsTopDirectory - 是否只在当前目录中查找,0 - 当前目录,非0 - 当前目录以及所有子目录
在控制流中编辑 Script Task - SCR_LoadAllFiles, 要分得清楚哪些是只读的,哪些是需要修改值 - 可读写类型。
记得引用 System.IO 命名空间,在 MAIN 方法中的代码 -
public void Main()
{
string directory = Dts.Variables["User::Directory"].Value.ToString();
int isTopDirectory = Int16.Parse(Dts.Variables["User::IsTopDirectory"].Value.ToString());
string extension = Dts.Variables["User::FileExtensionFilter"].Value.ToString();
// TODO: Add your code here
if (isTopDirectory == 0)
{
Dts.Variables["User::FileNames"].Value = Directory.GetFiles(directory, extension, SearchOption.TopDirectoryOnly);
}
else
{
Dts.Variables["User::FileNames"].Value = Directory.GetFiles(directory, extension, SearchOption.AllDirectories);
}
Dts.TaskResult = (int)ScriptResults.Success;
}
Foreach Loop Container 中 - 选择 Foreach From Variable Enumerator 这样就可以遍历一个数组集合了。
每次循环从 FileNames 集合中取到的一个文件名就赋值给 FileName。
下面要做的操作是 - 在循环中根据文件的后缀的不同拷贝到不同的文件夹。由于文件夹下可能有除了 .xml 和 .txt 之外的很多文件,因此在这里需要判断如果不是 .xml 和 .txt 文件类型的就忽略拷贝动作。问题在于:在控制流 Control Flow Task 中并没有像 Data Flow Task 数据流中所具有的像 Conditional Split 等控件,所以这里只能使用流程中的 Precedence Constraint 先决条件或者叫做先决约束来控制。
由于在 Copy Files to Directory 这个 File System Task 本身不具备这种过滤操作,因此主动的在它的前面添加一个 Expression Task 然后就可以使用 Precedence Constraint 了,实际上这个 Expression Task 本身不处理任何操作。(技巧三 - 如何在没有上游控件输出的情况下,在控制流中实现条件判断处理)。 Expression Task 中可以随便拖放一个变量 -
关键的还是在连接 Cope Files To Directory 的那条线上我们要编辑条件 -
在最开始定义 DestinationDirectory 变量的时候已经介绍过了,它的值是由其它变量决定的。这里,在每次循环的时候都可以拿到 FileName 文件名,然后如果在文件名中能查找到 .txt 那么 DestinationDirectory 的值就等于 TxtFileDirectory 的值,后面同理。如果都找不到,那么就等于空字符串。所以上面的判断就体现这样的逻辑,只有后缀是 .txt 或者 .xml 结尾的文件才会进入到下一步被处理。
在 Copy Files to Directory (File System Task) 中 先选择 Operation 然后再配置其它操作 -
当然 File System Task 不仅仅只可以用来操作 Cope Files,还有其它很多很多操作 -
最后保存执行 Package,它会遍历所有的子目录并获取所有的文件名称。
来自不同目录下的所有 .txt 文件集中到一个文件夹下。
来自不同目录的 .xml 文件集中到一个目录下。
那么利用上面的这些文件处理控件包括遍历等到底可以实现什么具体功能? 有一个需求可能会存在 - 就是有一个 JOB ,每天执行一次,然后监视一下指定目录下文档的变化情况,将变化的情况写入数据库表中,或者做成报表,可以非常容易的看到那些是今天新增加的文件,哪些文件发生了改变等等。
更多 BI 文章请参看 BI 系列随笔列表 (SSIS, SSRS, SSAS, MDX, SQL Server)
如果觉得这篇文章看了对您有帮助,请帮助推荐,以方便他人在 BIWORK 博客推荐栏中快速看到这些文章。