Sql server 当没有可下载的文件时,如何避免SSIS FTP任务失败?

Sql server 当没有可下载的文件时,如何避免SSIS FTP任务失败?,sql-server,ssis,ftp,Sql Server,Ssis,Ftp,我正在使用SQLServer2005,并在SSIS中创建ftp任务 有时会有文件通过ftp传输,有时不会。如果没有文件,我不希望任务或包失败。我将ftp任务中的箭头改为下一个“completion”,这样包就可以运行了。我已将允许的错误数更改为4(因为有4个ftp任务,4个目录中的任何一个都可能有文件,也可能没有文件) 但是,当我从代理中的作业运行包时,它会将作业标记为失败。由于这将每15分钟运行一次,我不希望在我的工作历史中出现一堆红色的x,这将导致我们在问题真正发生时看不到问题 如何在ftp

我正在使用SQLServer2005,并在SSIS中创建ftp任务

有时会有文件通过ftp传输,有时不会。如果没有文件,我不希望任务或包失败。我将ftp任务中的箭头改为下一个“completion”,这样包就可以运行了。我已将允许的错误数更改为4(因为有4个ftp任务,4个目录中的任何一个都可能有文件,也可能没有文件)

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

如何在ftp任务中设置属性,以便不将文件查找到ftp不会失败?我使用的操作是“发送文件”

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


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

我没有一个打包的答案给你,但是因为还没有其他人发布任何东西


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

将其放入ForEach容器中,该容器将迭代要上载的文件。无文件、无FTP、无故障。

您可以在出现故障时重定向到另一个不执行任何操作的任务,即返回true的脚本


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

Aha,好的-感谢您的澄清。由于FTP任务无法返回文件夹列表,因此不可能像我最初所说的那样使用ForEach-这仅在将X个文件上载到远程源时有效

要下载X数量的文件,可以采用两种方法,一种是在脚本任务中完全在.Net中完成,另一种是在.Net脚本任务中使用文件名填充ArrayList,然后在ArrayList上进行ForEach,将文件名传递给一个变量,然后在标准FTP任务中下载该变量名

适用的代码示例:

因此,在上面,您将获得FileNames()并从中填充ArrayList,然后将ArrayList分配给Dts.Variables中的一个对象类型变量,然后使用类似以下代码对该对象(ArrayList)变量进行ForEach:

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

这可能不是最好的解决方案,但这是可行的

我使用了一个脚本任务,并为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的新手,这可能是个难题。但它现在起作用了。我想我要求的是最好的方法,我当然不会声称这就是最好的方法

正如我指出的,我无法知道这些文件的名称,甚至根本不知道那里是否有任何文件。如果它们在那里,我想得到它们。

检查这篇文章,它描述了如何优雅地处理SSIS包中的任务错误

我几乎有同样的问题,但检索文件。我希望在FTP服务器上找不到文件时,包不会失败。上面的链接阻止错误冒泡并导致包失败;您可能认为FailPackageError=false应该执行的操作?:-


希望这也能帮你解决

您可以使用eaSkills提供的免费SSIS FTP任务+。如果一个或多个文件不存在,它不会抛出错误,它支持通配符,并提供下载和删除选项(如果需要)

以下是指向功能页的链接:
我知道您已经找到了问题的答案。这是为其他可能偶然发现这个问题的用户准备的。以下是实现这一目标的一种可能方法<代码>脚本任务可用于查找给定模式的FTP文件夹路径中存在的文件列表(例如
*.txt
)。下面的示例显示了如何做到这一点

逐步过程:

  • 在SSIS包上,创建一个
    FTP连接
    
    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;
    }
    
    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
    
    Dts.Variables["FTP_Error"].Value = "ErrorCode:" + Dts.Variables["ErrorCode"].Value.ToString() + ", ErrorDescription=" + Dts.Variables["ErrorDescription"].Value.ToString();
    
    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;
    }
    
        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;
        }