web-dev-qa-db-fra.com

Comment éviter l'échec de la tâche FTP de SSIS lorsqu'il n'y a aucun fichier à télécharger?

J'utilise SQL Server 2005 et crée des tâches ftp dans SSIS.

Parfois, il y aura des fichiers sur lesquels ftp over, parfois non. S'il n'y a pas de fichiers, je ne veux pas que la tâche ni le package échouent. J'ai changé la flèche allant de la tâche ftp à la suivante en "complétion", ainsi le paquet est exécuté. J'ai changé le nombre autorisé d'erreurs à 4 (car il y a 4 tâches ftp, et n'importe lequel des 4 répertoires peut contenir ou non des fichiers).

Mais, lorsque je lance le package à partir d'un travail dans l'agent, le travail échoue. Comme cela fonctionnera toutes les 15 minutes, je ne veux pas qu'il y ait beaucoup de x rouges dans mon historique de travail, ce qui nous évitera de voir un problème lorsqu'il se produit réellement.

Comment définir les propriétés dans la tâche ftp pour que la recherche de fichiers au format FTP ne soit pas un échec? L'opération que j'utilise est "Envoyer des fichiers".

Voici quelques informations supplémentaires: les fichiers se trouvent sur un serveur auquel je n’ai accès que par ftp. Et, je ne connais pas les noms de fichiers à l'avance. L'utilisateur peut les appeler comme ils veulent. Je ne peux donc pas vérifier des fichiers spécifiques, ni, je pense, du tout. Sauf en utilisant la connexion ftp et les tâches basées sur cette connexion. Les fichiers se trouvent sur un serveur distant et je souhaite les copier sur mon serveur pour les obtenir à partir de ce serveur distant.

Je peux Shell un ftp de niveau commande dans une tâche de script. C’est peut-être ce que j’ai besoin d’utiliser au lieu d’une tâche ftp. (J'ai changé pour utiliser la ligne de commande ftp, avec un fichier de paramètres, appelé à partir d'une tâche de script. Il ne génère aucune erreur s'il n'y a pas de fichiers à récupérer. Je pense que cette solution fonctionnera pour moi. fichier de paramètres dynamiquement, ce qui signifie que je n'ai pas besoin d'informations de connexion dans le fichier texte brut, mais que je peux plutôt le stocker dans mon fichier de configuration, qui se trouve dans un emplacement plus sécurisé.)

18
thursdaysgeek

Je comprends que vous ayez trouvé une réponse à votre question. Ceci est destiné aux autres utilisateurs qui pourraient trébucher sur cette question. Voici un moyen possible d'y parvenir. Script Task peut être utilisé pour trouver la liste des fichiers présents dans un chemin de dossier FTP pour un modèle donné (par exemple *.txt). L'exemple ci-dessous montre comment cela peut être fait.

Processus pas à pas:

  1. Sur le package SSIS, créez un FTP Connection nomméFTPet créez également les variables 5 comme indiqué dans la capture d'écran n ° 1 . La variable RemotePath contient le chemin du dossier FTP; LocalPath contient le dossier dans lequel les fichiers seront téléchargés; FilePattern contient le modèle de fichier permettant de trouver la liste des fichiers à télécharger à partir du serveur FTP; FileName sera renseigné par le Foreach loop container, mais pour éviter toute erreur de temps de conception d'une tâche FTP, il peut être rempli avec/ou la propriété DelayValidation de la tâche FTP peut être définie sur True .

  2. Sur le package SSIS, placez un Script Task, Foreach Loop container et FTP Task dans le Foreach Loop container, comme indiqué dans les captures d'écran # 2 .

  3. Remplacez la méthode Main() dans le Script Task par le code situé dans la section Code de tâche de script . La tâche de script remplira la variable ListOfFiles avec la collection de fichiers correspondant à un modèle donné. Cet exemple utilisera d’abord le motif * .txt, qui ne donne aucun résultat, puis le motif * .xls qui correspondra à quelques fichiers sur le serveur FTP.

  4. Configurez le Foreach Loop container comme indiqué dans les captures d’écran # 3 et # 4 . Cette tâche effectuera une boucle dans la variable ** ListOfFiles *. S'il n'y a pas de fichiers, la tâche FTP à l'intérieur du conteneur de boucles ne s'exécutera pas. S'il y a des fichiers, la tâche FTP à l'intérieur du conteneur de boucles s'exécutera pour la tâche en fonction du nombre de fichiers trouvés sur le serveur FTP.

  5. Configurez le FTP Task comme indiqué dans les captures d'écran # 5 et # 6 .

  6. La capture # 7 montre un exemple d’exécution du paquet lorsque no des fichiers correspondants sont trouvés pour le modèle *.txt.

  7. La capture d'écran # 8 montre le contenu du dossier C:\temp\ before exécution du paquet.

  8. La capture # 9 montre un exemple d’exécution de paquet lorsque des fichiers correspondants sont trouvés pour le modèle *.xls.

  9. La capture d'écran # 10 montre le contenu du chemin d'accès distant FTP /Practice/Directory_New.

  10. La capture d'écran # 11 montre le contenu du dossier C:\temp\ après exécution du paquet.

  11. La capture d'écran # 12 montre l'échec du package s'il est fourni avec un chemin incorrect Remote .

  12. La capture d'écran # 13 affiche le message d'erreur lié à l'échec du package.

J'espère que cela pourra aider.

Code de tâche de script:

C # code pouvant être utilisé dansSSIS 2008 and above

Incluez l'instruction using using System.Text.RegularExpressions;

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

VBcode pouvant être utilisé dansSSIS 2005 and above

Incluez l'instruction 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

Capture d'écran n ° 1:

1

Capture d'écran n ° 2:

2

Capture d'écran n ° 3:

3

Capture d'écran n ° 4:

4

Capture d'écran n ° 5:

5

Capture d'écran n ° 6:

6

Capture d'écran # 7:

7

Capture d'écran # 8:

8

Capture d'écran # 9:

9

Capture d'écran # 10:

10

Capture d'écran # 11:

11

Capture d'écran # 12:

12

Capture d'écran # 13:

13

14
user756519

Cochez cette link qui décrit la gestion des erreurs de tâche dans le package SSIS.

J'ai eu presque le même problème mais, avec la récupération de fichiers. Je voulais que le paquet n'échoue PAS lorsqu'aucun fichier n'a été trouvé sur le serveur FTP. Le lien ci-dessus empêche l'erreur de se propager et de provoquer l'échec du package; quelque chose que vous auriez pensé que FailPackageOnError = false aurait dû faire? : -S

J'espère que cela résoudra les problèmes pour vous aussi!

14
Rich

Je viens d'avoir ce problème, après avoir lu certaines des réponses ici, rien ne résout vraiment mon problème et les solutions ici semblent insensées en termes de complexité.

Ma tâche FTP échouait depuis que je n'autorisais pas l'écrasement des fichiers. Disons que le travail a été lancé deux fois de suite. Le premier passage sera correct, car certains fichiers sont transférés mais échoueront si un fichier local existe déjà. 

Ma solution était simple:

  1. Tâche de clic droit - Propriétés
  2. Set ForceExecutionResult = "Succès"
4
tribe84

(Je ne peux pas accepter ma propre réponse, mais c'est la solution qui a fonctionné pour moi.)

Ce n'est peut-être pas la meilleure solution, mais cela fonctionne.

J'utilise une tâche de script et dispose de nombreuses variables pour les informations de connexion FTP, ainsi que pour les répertoires source et de destination. (Parce que nous allons changer le serveur sur lequel il est exécuté et il sera plus facile de changer dans un paquet de configuration.)

Je crée un fichier texte à la volée et lui écrit les commandes 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()

Puis, après lui avoir laissé suffisamment de temps pour fermer, je lance un processus pour exécuter la commande ftp:

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

Puis, après avoir laissé un peu plus de temps au processus ftp, je supprime le fichier ftp temporaire (qui contient des informations de connexion!).

Si les fichiers n'existent pas dans le répertoire source (la variable a le mappage \\ lecteur\dir\*. *), Il n'y a pas d'erreur. Si une autre erreur se produit, la tâche échoue toujours, comme il se doit.

Je suis nouveau à SSIS, et cela peut être un kludge. Mais ça marche pour le moment. Je suppose que j'ai demandé le meilleur moyen, et je ne prétends certainement pas que c'est ça.

Comme je l'ai souligné, je n'ai aucun moyen de savoir comment les fichiers sont nommés, ni même s'il y a des fichiers. S'ils sont là, je veux les avoir.

3
thursdaysgeek

Je n'ai pas de réponse packagée pour vous, mais comme personne d'autre n'a encore rien posté ...

Vous devriez pouvoir définir une variable dans une tâche de script ActiveX, puis l'utiliser pour décider si la tâche FTP doit être exécutée ou non. Il y a un exemple ici qui fonctionne avec les chemins locaux. Espérons que vous pourrez adapter le concept (ou, si possible, mapper le lecteur FTP et le faire de cette façon).

1
Tom H

1) Définissez la propriété de la tâche FTP ForceExecutionResult = Success

2) Ajoutez ce code au gestionnaire d'événements OnError de la tâche FTP.

    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;
    }
1
Todd Hoffert

Placez-le dans un conteneur ForEach, qui itère sur les fichiers à télécharger. Pas de fichiers, pas de FTP, pas d'échec.

0
Meff

C'est une autre solution qui fonctionne pour moi, en utilisant des outils intégrés et donc sans réécrire manuellement la logique FTP:

1) Créez une variable dans votre paquetage appelée FTP_Error

2) Cliquez sur votre tâche FTP, puis sur l'onglet "Gestionnaires d'événements".

3) Cliquez dans la page pour créer un gestionnaire d’événements pour "FTP Task/OnError" - il se déclenchera chaque fois qu’un problème survient avec FTP

4) Dans la boîte à outils, faites glisser un élément de tâche de script et double-cliquez dessus pour l'ouvrir.

5) Dans la première fenêtre contextuelle, ReadOnlyVariables - ajoutez System :: ErrorCode, System :: ErrorDescription

6) Dans la première fenêtre contextuelle, ReadWriteVariables - ajoutez votre variable User :: FTP_Error

7) Modifier le script

8) Dans le script, définissez votre variable FTP_Error pour contenir les ReadOnlyVariables ci-dessus:

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

9) Sauvegarder et fermer le script

10) Appuyez sur "OK" pour la tâche de script

11) Retournez à l'onglet "Flux de contrôle"

12) À partir de la tâche FTP, OnError va à une nouvelle tâche de script et édite celle-ci.

13) ReadOnlyVariables: User :: FTP_Error d'avant

14) Maintenant, quand aucun fichier ne se trouve sur le FTP, le code d'erreur est -1073573501 (Vous pouvez trouver la liste de référence des codes d'erreur ici: http://msdn.Microsoft.com/en- us/library/ms345164.aspx )

15) Dans votre script, indiquez dans la logique de faire ce que vous voulez - si vous trouvez un code "aucun fichier trouvé", alors peut-être que vous dites que la tâche a réussi. Sinon, la tâche a échoué. Et votre flux normal peut gérer cela comme vous le souhaitez:

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

Et à partir de là, votre flux Succeeded/Failed fera ce que vous voulez en faire.

0
Chad

Vous pouvez rediriger en cas d’échec, vers une autre tâche qui ne fait rien, c’est-à-dire un script qui renvoie simplement true. 

Pour ce faire, ajoutez la nouvelle tâche de script, mettez en surbrillance votre tâche FTP, un deuxième connecteur vert apparaîtra, faites-le glisser sur la tâche de script, puis double-cliquez dessus. Sélectionnez Échec dans la liste déroulante Valeur. De toute évidence, vous devrez alors gérer les échecs réels dans cette tâche de script avant de les afficher directement dans l'historique des tâches.

0
baldy

Vous pouvez utiliser le logiciel gratuit SSIS FTP Task ++ de eaSkills. Il ne génère pas d'erreur si le ou les fichiers n'existent pas, il prend en charge les caractères génériques et vous offre la possibilité de télécharger et de supprimer le cas échéant.

Voici le lien vers la page de fonctionnalité: http://www.easkills.com/ssis/ftptask

0
Kilani

Une alternative consiste à utiliser cet énumérateur FTP File Enumeratorenter image description here

0
Joost

Aha, OK - Merci pour la clarification. Comme la tâche FTP ne peut pas renvoyer une liste de dossiers, il ne sera pas possible d’utiliser le ForEach comme je l’ai dit au début - Cela ne fonctionne que si vous téléchargez X nombre de fichiers sur une source distante.

Pour télécharger une quantité X de fichiers, vous pouvez procéder de deux manières: soit vous le faites entièrement en .Net dans une tâche de script, soit vous pouvez renseigner une liste de tableaux avec les noms de fichiers contenus dans une tâche de script .Net, puis ForEach sur la liste de tableaux , en passant le nom du fichier à une variable et en téléchargeant ce nom dans une tâche FTP standard.

Exemple de code correspondant: http://forums.Microsoft.com/msdn/ShowPost.aspx?PostID=2472491&SiteID=1

Ainsi, dans ce qui précède, vous obtiendrez le FileNames () et remplirez le ArrayList à partir de cela, puis assigner le ArrayList à une variable de type Object dans Dts.Variables, puis ForEach sur cette variable Object (ArrayList) en utilisant un code du type: http://www.sqlservercentral.com/articles/SSIS/64014/

0
Meff