18

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

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

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

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

以下是更多信息:文件位于除 ftp 之外我没有任何访问权限的服务器上。而且,我不提前知道文件名。用户可以随心所欲地称呼他们。所以我不能检查特定的文件,我想,我也不能检查。除非通过使用 ftp 连接和基于该连接的任务。这些文件位于远程服务器上,我想将它们复制到我的服务器上,以便从该远程服务器获取它们。

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

4

12 回答 12

14

检查此链接,该链接描述了有关在 SSIS 包中正常处理任务错误的信息。

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

希望这也能为您解决!

于 2008-11-20T18:08:26.520 回答
14

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

分步过程:

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

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

  3. Main()将 中的方法替换为脚本任务代码部分Script Task下的代码。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显示了执行包C:\temp\ 后文件夹的内容。

  11. 屏幕截图# 12显示了在提供不正确的远程路径时包失败。

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

希望有帮助。

脚本任务代码:

SSIS 2008 and above用于.

包含使用 System.Text.RegularExpressionsusing的语句;

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;
}

VB代码可用于SSIS 2005 and above.

包括Imports语句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

于 2011-07-16T07:14:00.050 回答
5

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

我的 FTP 任务失败,因为我不允许覆盖文件,假设该作业连续两次启动,第一次就可以了,因为有些文件已传输,但如果本地文件已经存在,则会失败。

我的解决方案很简单:

  1. 右键任务——属性
  2. 设置 ForceExecutionResult = "成功"
于 2015-10-16T13:37:48.003 回答
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 的新手,这可能是一个难题。但它现在有效。我想我要求最好的方法,我当然不会声称就是这样。

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

于 2008-10-02T23:16:09.013 回答
1

我没有给你一个打包的答案,但由于没有其他人发布任何内容......

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

于 2008-09-26T18:22:08.333 回答
1

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

2) 将此代码添加到 FTP 任务 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;
    }
于 2018-02-15T21:25:38.597 回答
0

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

要下载 X 数量的文件,您可以采用两种方式,您可以完全在 .Net 中的脚本任务中完成,或者您可以使用 .Net 脚本任务中的文件名填充 ArrayList,然后在 ArrayList 上使用 ForEach ,将文件名传递给变量并在标准 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/

于 2008-10-02T14:19:39.457 回答
0

将其放入 ForEach 容器中,该容器会遍历要上传的文件。没有文件,没有 FTP,没有失败。

于 2008-09-26T20:38:54.917 回答
0

另一种方法是使用此FTP 文件枚举器 在此处输入图像描述

于 2012-07-18T07:28:16.543 回答
0

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

为此,添加新的脚本任务,突出显示您的 FTP 任务,将出现第二个绿色连接器,将其拖到脚本任务,然后双击它。在值下拉列表中选择失败。显然,您需要在此脚本任务中处理真正的故障,才能在作业历史记录中正确显示。

于 2008-10-02T14:00:48.223 回答
0

您可以使用来自 eaSkills 的免费 SSIS FTP Task++。如果一个或多个文件不存在,它不会抛出错误,它支持通配符,如果需要,您可以选择下载和删除。

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

于 2010-03-10T06:39:53.687 回答
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)点击“确定”脚本任务

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

12) 从 FTP 任务,OnError 转到一个新的脚本任务,并编辑

13) ReadOnlyVariables: User::FTP_Error from before

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;
}

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

于 2012-02-20T15:20:13.003 回答