2015-12-30 2 views
3

J'utilise la méthode suivante pour copier le contenu d'un répertoire dans un répertoire différent.Méthode de copie de grandes quantités de données en C#

public void DirCopy(string SourcePath, string DestinationPath) 
    { 
     if (Directory.Exists(DestinationPath)) 
     { 
      System.IO.DirectoryInfo downloadedMessageInfo = new DirectoryInfo(DestinationPath); 

      foreach (FileInfo file in downloadedMessageInfo.GetFiles()) 
      { 
       file.Delete(); 
      } 
      foreach (DirectoryInfo dir in downloadedMessageInfo.GetDirectories()) 
      { 
       dir.Delete(true); 
      } 
     } 



     //================================================================================= 
     string[] directories = System.IO.Directory.GetDirectories(SourcePath, "*.*", SearchOption.AllDirectories); 

     Parallel.ForEach(directories, dirPath => 
     { 
      Directory.CreateDirectory(dirPath.Replace(SourcePath, DestinationPath)); 
     }); 

     string[] files = System.IO.Directory.GetFiles(SourcePath, "*.*", SearchOption.AllDirectories); 

     Parallel.ForEach(files, newPath => 
     { 
      File.Copy(newPath, newPath.Replace(SourcePath, DestinationPath), true); 
     }); 

    } 

Mon seul problème est qu'il ya un peu de données dans le chemin source et le programme devient non réactif alors que la copie est en cours. Je me demande quelles sont mes options pour copier des données. J'ai fait des recherches et quelqu'un a recommandé d'utiliser un tampon.

Je n'ai pas vraiment vu de solution que je comprends particulièrement bien, donc toute aide/ressources qui sont claires et concises serait géniale.

Merci pour toute aide!

+0

Vous pouvez utiliser un thread séparé pour exécuter au lieu de courir dans le thread d'interface utilisateur. –

+0

pouvez-vous quantifier certaines des choses que vous copiez? Comme les répertoires, les fichiers, la quantité de données que vous transférez, vous utilisez peut-être le mauvais outil pour le travail – konkked

+0

Est-ce une application d'interface utilisateur? Si c'est le cas, faites cela dans un thread d'arrière-plan. Par exemple via 'Task.Run' –

Répondre

2

Exécution des tâches longues dans Windows Forms, sur le fil de message entraîne la forme pour devenir ne répond pas jusqu'à ce que la tâche soit terminée. Vous allez avoir besoin d'utiliser le thread pour empêcher cela.Cela peut se compliquer, mais vous allez avoir besoin d'un BackgroundWorker:

_Worker = new BackgroundWorker(); 
_Worker.WorkerReportsProgress = true; 
_Worker.DoWork += Worker_DoWork; 
_Worker.ProgressChanged += Worker_ProgressChanged; 
_Worker.RunWorkerAsync(); 

Une méthode qui préformes la tâche:

private void Worker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    BackgroundWorker worker = sender as BackgroundWorker; 
    worker.ReportProgress(1); 

    // do stuff 

    worker.ReportProgress(100); 
} 

Et une méthode pour rendre compte des progrès:

private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    switch (e.ProgressPercentage) 
    { 
     case 1: 
      // make your status bar visible 
      break; 

     case 100: 
      // hide it again 
      break; 
    } 
} 

Vous pouvez utiliser une barre de progression de sélection, mais si vous voulez rapporter le pourcentage réel, le calcul de la taille et de la progression des fichiers dans votre méthode Worker_DoWork peut devenir compliqué et constitue un autre problème.

https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx

3

Si votre objectif est d'empêcher l'application de passer à un état non réactif, la méthode suggérée pour utiliser des tampons ne résoudra pas votre problème. Au lieu de cela, regardez en utilisant un Thread pour copier vos répertoires.

Encore mieux, utilisez un BackgroundWorker, ce qui présente l'avantage supplémentaire de pouvoir signaler des progrès.

+0

Oui, utilisez un travailleur d'arrière-plan. Votre application restera réactive, vous pouvez mettre à jour les variables dans le processus principal pour la progression et la sortie quand elle sera finie. –

2

Une solution rapide pour votre problème est d'utiliser un fil d'arrière-plan au code d'appel comme celui-ci:

var source_directory = "c:\\source"; 
var destination_directory= "c:\\destination"; 
Task.Run(() => DirCopy(source_directory, destination_directory)); 

Cet exemple utilise le Task.Run method qui utilise l'un des fils fil piscine pour exécuter le code.

Ceci permet de s'assurer que le thread de l'interface utilisateur est libre de mettre à jour l'interface utilisateur et de répondre aux entrées de l'utilisateur.

1

Si l'on suppose que le problème réel est votre programme devenir non réactif ...

Votre programme ne répond probablement parce que le fil que vous utilisez pour effectuer les copies est le fil que vous levier pour répondre à l'entrée de l'utilisateur. Si vous souhaitez que les copies continuent en arrière-plan pendant que le programme reste réactif, vous devez effectuer les copies de manière asynchrone. (Je suppose que vous utilisez winforms ou wpf en fonction de votre contexte.)

L'approche typique consiste simplement à faire tourner un travailleur d'arrière-plan, expédier le travail de copie et le laisser aller à la ville pendant que votre gui thread répond à l'entrée de l'utilisateur. Il existe d'autres techniques plus sophistiquées avec de meilleurs compromis, mais je suppose que cela suffira pour votre scénario basé sur ce que vous avez décrit.

(Votre Parallel.ForEach ne fait pas l'affaire parce que le fil qui déclenche ne va pas continuer jusqu'à ce que le Parallel.ForEach a terminé son exécution)

2

On ne sait pas quelle version du compilateur/cadre que vous utilisez, mais vous pouvez utiliser les opérations de fichiers asynchrones et ne pas avoir à vous soucier de filetage. Vous pouvez également utiliser les versions de streaming EnumerateDirectories et EnumerateFiles si vous avez de grandes hiérarchies de fichiers.

public async Task DirCopy(string SourcePath, string DestinationPath) 
{ 
    //slightly different from your code, in that the destination directory is simply removed recursively 
    Directory.Delete(DestinationPath, true); 

    //enumerate files returns earlier than get files for large file hierarchies 
    //... because it is a streaming IEnumerable instead of an array 
    foreach (var sourcePath in System.IO.Directory.EnumerateFiles(SourcePath, "*.*", SearchOption.AllDirectories)) 
    { 
     var destinationPath = sourcePath.Replace(SourcePath, DestinationPath); 

     //slightly different from your code, in that directories are created as needed 
     //... however, this would mean empty directories are not copied 
     Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); 

     using (var source = File.Open(sourcePath, FileMode.Open, FileAccess.Read)) 
     using (var destination = File.Create(destinationPath)) 
     { 
      //async copy of the file frees the current thread 
      //... e.g. for the UI thread to process UI updates 
      await source.CopyToAsync(destination); 
     } 
    } 
} 
0

Merci à tous, j'apprécie vraiment toutes les contributions pour voir les différentes façons d'y parvenir. Pour le moment j'ai décidé de faire un Task.Run mais je vais me pencher sur les opérations de fond et d'asynch.

Merci encore à tous!

Pour référence, je viens de faire

Task.Run(()=>{ DirCopy("source","destination"); }); 

DirCopy

public void DirCopy(string SourcePath, string DestinationPath) 
    { 
     if (Directory.Exists(DestinationPath)) 
     { 
      System.IO.DirectoryInfo downloadedMessageInfo = new DirectoryInfo(DestinationPath); 

      foreach (FileInfo file in downloadedMessageInfo.GetFiles()) 
      { 
       file.Delete(); 
      } 
      foreach (DirectoryInfo dir in downloadedMessageInfo.GetDirectories()) 
      { 
       dir.Delete(true); 
      } 
     } 



     //================================================================================= 
     string[] directories = System.IO.Directory.GetDirectories(SourcePath, "*.*", SearchOption.AllDirectories); 
     string[] files = System.IO.Directory.GetFiles(SourcePath, "*.*", SearchOption.AllDirectories); 

     totalPB.Minimum = 0; 
     totalPB.Maximum = directories.Length; 
     totalPB.Value = 0; 
     totalPB.Step = 1; 

     subTotalPB.Minimum = 0; 
     subTotalPB.Maximum = directories.Length; 
     subTotalPB.Value = 0; 
     subTotalPB.Step = 1; 

     Parallel.ForEach(directories, dirPath => 
     { 
      Directory.CreateDirectory(dirPath.Replace(SourcePath, DestinationPath)); 
      subTotalPB.PerformStep(); 
     }); 

     Task.Run(() => 
     { 

      Parallel.ForEach(files, newPath => 
      { 
       File.Copy(newPath, newPath.Replace(SourcePath, DestinationPath), true); 
       totalPB.PerformStep(); 
      }); 

     }); 



    }