如果没有要下载的文件,如何避免SSIS FTP任务失败?

时间:2008-09-26 17:36:37

标签: sql-server ssis ftp

我正在使用SQL Server 2005,并在SSIS中创建ftp任务。

有时会有ftp文件,有时不会。如果没有文件,我不希望任务和包失败。我已经将箭头从ftp任务更改为下一个“完成”,因此包运行完毕。我已将允许的错误数量更改为4(因为有4个ftp任务,并且4个目录中的任何一个可能有也可能没有文件)。

但是,当我从代理中的作业运行包时,它将作业标记为失败。由于这将每15分钟运行一次,我不希望在我的工作历史中出现一堆红色x,这将导致我们在确实发生时不会发现问题。

如何在ftp任务中设置属性,以便不向ftp查找文件不是故障?我正在使用的操作是“发送文件”。

以下是一些更多信息:文件位于服务器上,除了ftp之外我没有任何访问权限。而且,我提前不知道文件名。用户可以随意调用它们。所以我不能检查具体的文件,我认为,我可以检查。除了通过使用基于该连接的ftp连接和任务。这些文件位于远程服务器上,我想将它们复制到我的服务器上,以便从远程服务器上获取它们。

我可以在脚本任务中填充命令级别ftp。也许这就是我需要使用而不是ftp任务。 (我已经更改为使用ftp命令行,带有一个参数文件,从脚本任务调用。当没有文件可以获取时,它没有错误。我认为这个解决方案对我有用。我正在创建动态参数文件,这意味着我不需要在纯文本文件中有连接信息,而是可以存储在我的配置文件中,该文件位于更安全的位置。)

12 个答案:

答案 0 :(得分:14)

选中此link,其中介绍了如何在SSIS包中正常处理任务错误。

我有几乎相同的问题但是,检索文件。我希望在FTP服务器上找不到文件时包不会失败。上面的链接会阻止错误冒泡并导致程序包失败;你会想到FailPackageOnError = false应该做的事情吗? :-S

希望这也能为你解决它!

答案 1 :(得分:14)

我知道您找到了问题的答案。这适用于可能偶然发现此问题的其他用户。这是实现这一目标的一种可能方式。 Script Task可用于查找给定模式的FTP文件夹路径中存在的文件列表(例如*.txt)。下面的例子说明了如何做到这一点。

分步流程:

  1. 在SSIS包上,创建名为 FTP FTP Connection,并创建 5 变量,如屏幕截图# 1 。变量RemotePath包含FTP文件夹路径; LocalPath包含文件将被下载到的文件夹; FilePattern包含用于查找要从FTP服务器下载的文件列表的文件模式; FileName将填充Foreach loop container,但为了避免FTP任务设计时错误,可以使用 / 填充它,或者FTP任务上的DelayValidation属性可以填充设置为 True

  2. 在SSIS包中,在Script Task中放置Foreach Loop containerFTP TaskForeach Loop container,如屏幕截图# 2 所示

  3. Main()内的Script Task方法替换为脚本任务代码部分下的代码。脚本任务将使用与给定模式匹配的文件集合填充变量 ListOfFiles 。这个例子将首先使用模式* .txt,它不会产生任何结果,然后是模式* .xls,它将匹配FTP服务器上的几个文件。

  4. 配置Foreach Loop container,如屏幕截图# 3 和# 4 所示。此任务将循环遍历变量** ListOfFiles *。如果没有文件,循环容器内的FTP任务将不会执行。如果有文件,循环容器内的FTP任务将执行FTP服务器上找到的文件数的任务。

  5. 配置FTP Task,如屏幕截图# 5 和# 6 所示。

  6. 屏幕截图# 7 显示了为模式*.txt找到匹配文件时的示例包执行情况。

  7. 屏幕截图# 8 显示执行包之前文件夹C:\temp\ 的内容。

  8. 屏幕截图# 9 显示为模式*.xls找到匹配文件时的示例包执行。

  9. 屏幕截图# 10 显示FTP远程路径/Practice/Directory_New的内容。

  10. 屏幕截图# 11 显示执行

  11. 后文件夹C:\temp\ 的内容。

  12. 屏幕截图# 12 显示提供错误远程路径时的程序包失败。

  13. 屏幕截图# 13 显示与程序包失败相关的错误消息。

  14. 希望有所帮助。

    脚本任务代码:

    可以在 SSIS 2008 and above 中使用的

    C#代码。

    使用System.Text.RegularExpressions包含using语句;

    public void Main()
    {
        Variables varCollection = null;
        ConnectionManager ftpManager = null;
        FtpClientConnection ftpConnection = null;
        string[] fileNames = null;
        string[] folderNames = null;
        System.Collections.ArrayList listOfFiles = null;
        string remotePath = string.Empty;
        string filePattern = string.Empty;
        Regex regexp;
        int counter;
    
        Dts.VariableDispenser.LockForWrite("User::RemotePath");
        Dts.VariableDispenser.LockForWrite("User::FilePattern");
        Dts.VariableDispenser.LockForWrite("User::ListOfFiles");
        Dts.VariableDispenser.GetVariables(ref varCollection);
    
        try
        {
            remotePath = varCollection["User::RemotePath"].Value.ToString();
            filePattern = varCollection["User::FilePattern"].Value.ToString();
    
            ftpManager = Dts.Connections["FTP"];
            ftpConnection = new FtpClientConnection(ftpManager.AcquireConnection(null));
            ftpConnection.Connect();
            ftpConnection.SetWorkingDirectory(remotePath);
            ftpConnection.GetListing(out folderNames, out fileNames);
            ftpConnection.Close();
    
            listOfFiles = new System.Collections.ArrayList();
            if (fileNames != null)
            {
                regexp = new Regex("^" + filePattern + "$");
                for (counter = 0; counter <= fileNames.GetUpperBound(0); counter++)
                {
                    if (regexp.IsMatch(fileNames[counter]))
                    {
                        listOfFiles.Add(remotePath + fileNames[counter]);
                    }
                }
            }
    
            varCollection["User::ListOfFiles"].Value = listOfFiles;
        }
        catch (Exception ex)
        {
            Dts.Events.FireError(-1, string.Empty, ex.ToString(), string.Empty, 0);
            Dts.TaskResult = (int) ScriptResults.Failure;
        }
        finally
        {
            varCollection.Unlock();
            ftpConnection = null;
            ftpManager = null;
        }
    
        Dts.TaskResult = (int)ScriptResults.Success;
    }
    
    可以在 SSIS 2005 and above 中使用的

    VB 代码。

    包含Imports声明导入System.Text.RegularExpressions

    Public Sub Main()
        Dim varCollection As Variables = Nothing
        Dim ftpManager As ConnectionManager = Nothing
        Dim ftpConnection As FtpClientConnection = Nothing
        Dim fileNames() As String = Nothing
        Dim folderNames() As String = Nothing
        Dim listOfFiles As Collections.ArrayList
        Dim remotePath As String = String.Empty
        Dim filePattern As String = String.Empty
        Dim regexp As Regex
        Dim counter As Integer
    
        Dts.VariableDispenser.LockForRead("User::RemotePath")
        Dts.VariableDispenser.LockForRead("User::FilePattern")
        Dts.VariableDispenser.LockForWrite("User::ListOfFiles")
        Dts.VariableDispenser.GetVariables(varCollection)
    
        Try
    
            remotePath = varCollection("User::RemotePath").Value.ToString()
            filePattern = varCollection("User::FilePattern").Value.ToString()
    
            ftpManager = Dts.Connections("FTP")
            ftpConnection = New FtpClientConnection(ftpManager.AcquireConnection(Nothing))
    
            ftpConnection.Connect()
            ftpConnection.SetWorkingDirectory(remotePath)
            ftpConnection.GetListing(folderNames, fileNames)
            ftpConnection.Close()
    
            listOfFiles = New Collections.ArrayList()
            If fileNames IsNot Nothing Then
                regexp = New Regex("^" & filePattern & "$")
                For counter = 0 To fileNames.GetUpperBound(0)
                    If regexp.IsMatch(fileNames(counter)) Then
                        listOfFiles.Add(remotePath & fileNames(counter))
                    End If
                Next counter
            End If
    
            varCollection("User::ListOfFiles").Value = listOfFiles
    
            Dts.TaskResult = ScriptResults.Success
    
        Catch ex As Exception
            Dts.Events.FireError(-1, String.Empty, ex.ToString(), String.Empty, 0)
            Dts.TaskResult = ScriptResults.Failure
        Finally
            varCollection.Unlock()
            ftpConnection = Nothing
            ftpManager = Nothing
        End Try
    
        Dts.TaskResult = ScriptResults.Success
    End Sub
    

    屏幕截图#1:

    1

    屏幕截图#2:

    2

    屏幕截图#3:

    3

    屏幕截图#4:

    4

    屏幕截图#5:

    5

    屏幕截图#6:

    6

    屏幕截图#7:

    7

    屏幕截图#8:

    8

    屏幕截图#9:

    9

    屏幕截图#10:

    10

    屏幕截图#11:

    11

    屏幕截图#12:

    12

    屏幕截图#13:

    13

答案 2 :(得分:4)

我刚刚遇到这个问题,在阅读了一些回复之后,没有什么能真正解决我的问题,而且这里的解决方案在复杂性方面似乎很疯狂。

我的FTP任务失败,因为我不允许覆盖文件,假设作业连续两次启动,第一次传递会没问题,因为某些文件已经转移但如果本地文件已经存在则会失败。

我的解决方案很简单:

  1. 右键单击任务 - 属性
  2. 设置ForceExecutionResult =“成功”

答案 3 :(得分:3)

(我不能接受我自己的答案,但这是对我有用的解决方案。)

这可能不是最佳解决方案,但这可行。

我使用脚本任务,并为ftp连接信息以及源和目标目录提供了一堆变量。 (因为,我们将更改运行的服务器,并且在配置包中更改会更容易。)

我动态创建一个文本文件,并将ftp命令写入其中:

    Dim ftpStream As StreamWriter = ftpFile.CreateText()
    ftpStream.WriteLine(ftpUser)
    ftpStream.WriteLine(ftpPassword)
    ftpStream.WriteLine("prompt off")
    ftpStream.WriteLine("binary")
    ftpStream.WriteLine("cd " & ftpDestDir)
    ftpStream.WriteLine("mput " & ftpSourceDir)
    ftpStream.WriteLine("quit 130")
    ftpStream.Close()

然后,在给它足够的时间关闭之后,我开始执行ftp命令的过程:

    ftpParameters = "-s:" & ftpParameterLoc & ftpParameterFile & " " & ftpServer
    proc = System.Diagnostics.Process.Start("ftp", ftpParameters)

然后,在给它更多的时间让ftp进程运行之后,我删除了临时的ftp文件(其中包含连接信息!)。

如果源目录中不存在文件(变量具有\\ drive \ dir \ *。*映射),则没有错误。如果发生其他错误,任务仍然会失败,应该如此。

我是SSIS的新手,这可能是一个混乱。但它现在有效。我想我问过最好的方法,我肯定不会说这就是它。

正如我所指出的,我无法知道文件的名称,或者根本没有任何文件。如果他们在那里,我想得到他们。

答案 4 :(得分:1)

我没有给你打包的答案,但是因为还没有其他人发布任何内容......

您应该能够在ActiveX脚本任务中设置变量,然后使用它来决定是否应该运行FTP任务。有一个示例here适用于本地路径。希望您能够调整概念(或者如果可能的话,映射FTP驱动器并按照这种方式进行)。

答案 5 :(得分:1)

1)设置FTP任务属性ForceExecutionResult = Success

2)将此代码添加到FTP Task OnError事件处理程序。

    public void Main()
    {
        // TODO: Add your code here

        int errorCode = (int)Dts.Variables["System::ErrorCode"].Value;

        if (errorCode.ToString().Equals("-1073573501"))
        {
            Dts.Variables["System::Propagate"].Value = false;
        }
        else
        {
            Dts.Variables["System::Propagate"].Value = true;
        }


        Dts.TaskResult = (int)ScriptResults.Success;
    }

答案 6 :(得分:0)

将它放在ForEach容器中,该容器迭代要上载的文件。没有文件,没有FTP,没有失败。

答案 7 :(得分:0)

您可以将失败重定向到另一个不执行任务的任务,即只返回true的脚本。

要执行此操作,请添加新脚本任务,突出显示您的FTP任务,将出现第二个绿色连接器,将其拖动到脚本任务,然后双击它。在“值”下拉列表中选择“失败”。显然,您需要处理此脚本任务中的实际故障,以便仍然显示在作业历史记录中。

答案 8 :(得分:0)

啊哈,好的 - 谢谢你的澄清。由于FTP任务无法返回文件夹列表,因此我无法像我最初所说的那样使用ForEach - 只有在您将X数量的文件上传到远程源时才有效。

要下载X个文件,您可以采用两种方式,既可以在脚本任务中完全使用.Net,也可以使用.Net脚本任务中的文件名填充ArrayList,然后使用ForEach在ArrayList上,将文件名传递给变量并在标准FTP任务中下载该变量名。

适合的代码示例:http://forums.microsoft.com/msdn/ShowPost.aspx?PostID=2472491&SiteID=1

因此,在上面,您将获取FileNames()并从中填充ArrayList,然后将ArrayList分配给Dts.Variables中的Object类型变量,然后使用代码将ForEach分配给Object(ArrayList)变量喜欢:http://www.sqlservercentral.com/articles/SSIS/64014/

答案 9 :(得分:0)

您可以使用eaSkills提供的免费SSIS FTP Task ++。如果文件不存在,它不会引发错误,它支持通配符,并且如果您需要,可以选择下载和删除。

以下是功能页面的链接: http://www.easkills.com/ssis/ftptask

答案 10 :(得分:0)

这是另一个适用于我的解决方案,使用内置的东西,因此无需手动重写FTP逻辑:

1)在包中创建一个名为FTP_Error

的变量

2)单击您的FTP任务,然后单击“事件处理程序”选项卡

3)在页面中单击以创建“FTP Task / OnError”的事件处理程序 - 只要FTP出现问题就会触发

4)从工具箱中,拖动“脚本任务”项,然后双击打开它

5)在第一个弹出窗口中,ReadOnlyVariables - 添加System :: ErrorCode,System :: ErrorDescription

6)在第一个弹出窗口中,ReadWriteVariables - 添加你的User :: FTP_Error变量

7)编辑脚本

8)在脚本中设置你的FTP_Error变量来保存我们上面的ReadOnlyVariables:

Dts.Variables["FTP_Error"].Value = "ErrorCode:" + Dts.Variables["ErrorCode"].Value.ToString() + ", ErrorDescription=" + Dts.Variables["ErrorDescription"].Value.ToString();

9)保存并关闭脚本

10)点击“OK”进行脚本任务

11)返回“控制流程”选项卡

12)在FTP任务中,OnError转到新的脚本任务,然后编辑

13)ReadOnlyVariables:之前的用户:: FTP_Error

14)现在,当FTP上没有找到文件时,错误代码为-1073573501 (您可以在此处找到错误代码参考列表:http://msdn.microsoft.com/en-us/library/ms345164.aspx

15)在你的脚本中,输入逻辑来做你想要的 - 如果你找到“找不到文件”的代码,那么也许你说任务成功了。如果没有,则任务失败。您的正常流程可以按照您的意愿处理:

if (Dts.Variables["FTP_Error"].Value.ToString().Contains("-1073573501"))
{
  // file not found - not a problem
  Dts.TaskResult = (int)ScriptResults.Success;
}
else
{
  // some other error - raise alarm!
  Dts.TaskResult = (int)ScriptResults.Failure;
}

从那里你的成功/失败的流程会做你想做的事情。

答案 11 :(得分:0)

另一种方法是使用此FTP File Enumerator enter image description here