C# - FileCopy

C# - FileCopy - C#/.NET managed - Programmation

Marsh Posté le 23-12-2009 à 17:20:18    

Bonjour,
 
Je suis débutant en C#; et en train de travailler sur une appli console chargé de copier plusieurs fichiers d'un dossier vers un autre. J'essaye vainement d'afficher un pourcentage ou autre indicateur de la progression de la copy. (FileSystemWatcher, StopWatch). File.copy ne permet que de connaitre la taille avant après mais pas pendant. Je ne souhaite pas utiliser de P/Invoke
Je me suis alors orienté vers la copy via Stream avec une taille de buffer.  
J'arrive à copier un fichier vers une destination avec une taille de 1024 par exemple mais je n'arrive pas à trouver un moyen de récuperer la taille de l'OutSream disons par exemple tout les 1/100 de la taille du fichier. Le but serait d'afficher un "=" ou % à chaque fois que la taille de l'Outstream a augmenté d'un bloc de 6MO pour un fichier de 600MO par exemple.
 
Si quelqu'un peut m'aider ou a déjà eu affaire à cà ?!
 
Merci  

Reply

Marsh Posté le 23-12-2009 à 17:20:18   

Reply

Marsh Posté le 24-12-2009 à 12:15:36    

BackgroundWorker !


---------------
Hobby eien /人◕ ‿‿ ◕人\
Reply

Marsh Posté le 26-12-2009 à 13:10:23    

Tamahome a écrit :

BackgroundWorker !


L'auteur du thread parle d'appli console, le BGWorker est un composant winform il me semble... Et il ne résoud pas le problème.
 
Si au lieu d'utiliser un simple Copy tu implémentais toi même une écriture tamponnée pas trop bête je pense que tu pourrais très bien gérer la progression de la copie comme tu le souhaites!


---------------
Topic .Net - C# @ Prog
Reply

Marsh Posté le 26-12-2009 à 22:22:48    

Pareil, c'est aussi ce que j'avais compris, mais il a l'air de dire qu'il n'y arrive pas :D.  
Je voulais juste confirmer que j'aurai suivi (à tort ?) la même direction, et que je ne voyais pas la difficulté, il nous faudrait des détails ou du code...


---------------
Topic .Net - C# @ Prog
Reply

Marsh Posté le 28-12-2009 à 16:37:33    

Un truc de ce genre, non ?

 
Code :
  1. using System;
  2. using System.IO;
  3.  
  4. namespace TestConsole
  5. {
  6.    struct Point
  7.    {
  8.        public int X;
  9.        public int Y;
  10.  
  11.        public Point(int x, int y)
  12.        {
  13.            X = x;
  14.            Y = y;
  15.        }
  16.  
  17.    }
  18.  
  19.    class Program
  20.    {
  21.        static void Main(string[] args)
  22.        {
  23.            Point cpos = new Point(Console.CursorLeft, Console.CursorTop);
  24.  
  25.            FileStream fs = File.OpenRead("c:\\in\\source.dat" );
  26.            FileStream fd = File.Create("c:\\in\\destination.dat" );
  27.            double totallength = (double)fs.Length;
  28.            double filepos = (double)fs.Position;
  29.  
  30.            byte[] buffer = new byte[1024];
  31.  
  32.            int length;
  33.            int percent = 0;
  34.            int oldpercent = 0;
  35.  
  36.            Console.Write("  0 %" );
  37.  
  38.            while ((length = fs.Read(buffer, 0, buffer.Length)) > 0)
  39.            {
  40.                filepos = (double)fs.Position;
  41.                fd.Write(buffer, 0, length);
  42.                percent = (int)((filepos / totallength) * 100);
  43.                if (percent != oldpercent)
  44.                {
  45.                    Console.SetCursorPosition(cpos.X, cpos.Y);
  46.                    Console.Write(string.Format("{0} %", percent).PadLeft(5, ' '));
  47.                    oldpercent = percent;
  48.                }
  49.            }
  50.  
  51.            Console.WriteLine();
  52.            Console.WriteLine("Terminé" );
  53.            Console.ReadKey(true);
  54.        }
  55.    }
  56. }


Message édité par MagicBuzz le 28-12-2009 à 17:41:50
Reply

Marsh Posté le 28-12-2009 à 17:36:01    

Ben je suis au boulot, mais on a un prestataire très frileux qui nous a tout planté notre projet de migration d'ERP, alors voilà quoi, je suis au chômage technique :D

Reply

Marsh Posté le 28-12-2009 à 17:51:13    

Seul petit reproche a ton MagicSource, je prefere :

Code :
  1. FileStream fs = File.OpenRead(@"c:\in\source.dat" );
  2. FileStream fd = File.Create(@"c:\in\destination.dat" );


a tes doubles \ mais je suppose que c'est une question de gout :)

Reply

Marsh Posté le 31-12-2009 à 12:07:34    

Just for fun, je me suis amusé à rendre le truc multi-threadé avec une jolie GUI et des performances accrues du coup, puisqu'un buffer de 1 Mo permet de pallier aux lenteurs ponctuelles I/O entre les deux threads consommateurs et producteurs.

 

Program.cs

Code :
  1. using System;
  2. using System.IO;
  3. using AdvancedFileManager;
  4.  
  5.  
  6. namespace TestConsole
  7. {
  8.    class Program
  9.    {
  10.        static void Main(string[] args)
  11.        {
  12.            MainProgram mp = new MainProgram();
  13.            mp.Run();
  14.        }
  15.    }
  16.  
  17.    class MainProgram
  18.    {
  19.        int lineSource = 0;
  20.        int lineDestination = 0;
  21.  
  22.        public MainProgram()
  23.        {
  24.        }
  25.  
  26.        public void Run()
  27.        {
  28.            Console.WriteLine("Lancement des threads..." );
  29.  
  30.            Console.WriteLine();
  31.  
  32.            lineSource = Console.CursorTop;
  33.            lineDestination = lineSource + 1;
  34.  
  35.            AdvancedFileManager.AdvancedFileManager afm = new AdvancedFileManager.AdvancedFileManager();
  36.            afm.SourceFileCopyEvent += new FileCopyEventHandler(afm_SourceFileCopyEvent);
  37.            afm.DestinationFileCopyEvent += new FileCopyEventHandler(afm_DestinationFileCopyEvent);
  38.  
  39.            try
  40.            {
  41.                afm.AsyncCopy(@"c:\in\source.dat", @"c:\in\destination.dat", true);
  42.            }
  43.            catch (Exception e)
  44.            {
  45.                Console.WriteLine("Une erreur s'est produite..." );
  46.                Console.WriteLine(e.Message);
  47.            }
  48.  
  49.            Console.SetCursorPosition(0, lineDestination + 2);
  50.  
  51.            Console.WriteLine("Terminé" );
  52.            Console.ReadKey(true);
  53.        }
  54.  
  55.        void afm_SourceFileCopyEvent(object sender, FileCopyEventArgs e)
  56.        {
  57.            DrawPercent(lineSource, e.Percent, "Source:" );
  58.        }
  59.  
  60.        void afm_DestinationFileCopyEvent(object sender, FileCopyEventArgs e)
  61.        {
  62.            DrawPercent(lineDestination, e.Percent, "Destination:" );
  63.        }
  64.  
  65.        public void DrawPercent(int line, int percent, string prefix)
  66.        {
  67.            lock (this)
  68.            {
  69.                Console.SetCursorPosition(0, line);
  70.                Console.Write("{0} [{1}] {2}%", prefix.PadRight(13, ' '), new string('█', (percent / 5)).PadRight(20, '='), percent.ToString().PadLeft(3, ' '));
  71.            }
  72.        }
  73.    }
  74. }
 

AdvancedFileManager.cs

Code :
  1. using System;
  2. using System.Collections;
  3. using System.IO;
  4. using System.Threading;
  5.  
  6. namespace AdvancedFileManager
  7. {
  8.    public delegate void FileCopyEventHandler(object sender, FileCopyEventArgs e);
  9.  
  10.    public class FileCopyEventArgs : EventArgs
  11.    {
  12.        public int Percent;
  13.  
  14.        public FileCopyEventArgs(int percent)
  15.        {
  16.            Percent = percent;
  17.        }
  18.    }
  19.  
  20.    public class AdvancedFileManager
  21.    {
  22.        public event FileCopyEventHandler SourceFileCopyEvent;
  23.        public event FileCopyEventHandler DestinationFileCopyEvent;
  24.  
  25.        public const int BUFFER_SIZE = 1024;
  26.        public const int QUEUE_SIZE = BUFFER_SIZE * 20;
  27.  
  28.        public void AsyncCopy(string source, string destination, bool overwrite)
  29.        {
  30.            // Usual verifications
  31.            if (!File.Exists(source))
  32.            {
  33.                throw new FileNotFoundException("Source file does not exists!" );
  34.            }
  35.  
  36.            if (File.Exists(destination) && !overwrite)
  37.            {
  38.                throw new Exception("Destination file already exists but overwrite is disabled!" );
  39.            }
  40.  
  41.            Queue queue = new Queue();
  42.  
  43.            // Ajouter les messages d'erreur propre en cas de problème d'accès aux fichiers
  44.            FileProducer Prod = new FileProducer(queue, source);
  45.            FileConsumer Cons = new FileConsumer(queue, destination, Prod.Length);
  46.  
  47.            Prod.FileCopyEvent += new FileCopyEventHandler(Prod_FileCopyEvent);
  48.            Cons.FileCopyEvent += new FileCopyEventHandler(Cons_FileCopyEvent);
  49.  
  50.            Thread Producer = new Thread(new ThreadStart(Prod.ThreadRun));
  51.            Thread Consumer = new Thread(new ThreadStart(Cons.ThreadRun));
  52.  
  53.            try
  54.            {
  55.                Producer.Start();
  56.                Consumer.Start();
  57.  
  58.                Producer.Join();
  59.                Consumer.Join();
  60.            }
  61.            catch
  62.            {
  63.                throw new Exception("Unhandled exception while running threads" );
  64.            }
  65.        }
  66.  
  67.        private void Prod_FileCopyEvent(object sender, FileCopyEventArgs e)
  68.        {
  69.            OnSourceFileCopyNotification(new FileCopyEventArgs(e.Percent));
  70.        }
  71.  
  72.        private void Cons_FileCopyEvent(object sender, FileCopyEventArgs e)
  73.        {
  74.            OnDestinationFileCopyNotification(new FileCopyEventArgs(e.Percent));
  75.        }
  76.  
  77.        protected virtual void OnSourceFileCopyNotification(FileCopyEventArgs e)
  78.        {
  79.            SourceFileCopyEvent(this, e);
  80.        }
  81.  
  82.        protected virtual void OnDestinationFileCopyNotification(FileCopyEventArgs e)
  83.        {
  84.            DestinationFileCopyEvent(this, e);
  85.        }
  86.  
  87.        private class FileProducer
  88.        {
  89.            public double Length;
  90.  
  91.            FileStream fs;
  92.            string filename;
  93.            Queue q;
  94.            int oldPercent = 0, percent = 0;
  95.  
  96.            public event FileCopyEventHandler FileCopyEvent;
  97.  
  98.            public FileProducer(Queue queue, string source)
  99.            {
  100.                q = queue;
  101.                filename = source;
  102.  
  103.                FileInfo fi = new FileInfo(filename);
  104.                Length = (double)fi.Length;
  105.            }
  106.  
  107.            public void ThreadRun()
  108.            {
  109.                fs = File.OpenRead(filename);
  110.  
  111.                byte[] buffer = new byte[BUFFER_SIZE];
  112.                int length;
  113.  
  114.                while ((length = fs.Read(buffer, 0, BUFFER_SIZE)) > 0)
  115.                {
  116.                    
  117.                    Monitor.Enter(q);
  118.  
  119.                    while (q.Count > QUEUE_SIZE)
  120.                    {
  121.                        // Wait until the consummer consume some data in the buffer
  122.                        Monitor.Wait(q);
  123.                    }
  124.  
  125.                    percent = (int)((double)fs.Position / Length * 100);
  126.                    if (percent != oldPercent)
  127.                    {
  128.                        OnFileCopyNotification(new FileCopyEventArgs(percent));
  129.                        oldPercent = percent;
  130.                    }
  131.  
  132.                    if (length < BUFFER_SIZE)
  133.                    {
  134.                        byte[] b = new byte[length];
  135.                        for (int i = 0; i < length; i++)
  136.                        {
  137.                            b[i] = buffer[i];
  138.                        }
  139.                        q.Enqueue(b);
  140.                    }
  141.                    else
  142.                    {
  143.                        q.Enqueue(buffer.Clone());
  144.                    }
  145.  
  146.                    Monitor.Pulse(q);
  147.                    Monitor.Exit(q);
  148.                }
  149.  
  150.                Monitor.Enter(q);
  151.                // Don't care is Queue is full, as it's the last item and it's 0-length
  152.                q.Enqueue(new byte[0]);
  153.                Monitor.Pulse(q);
  154.                Monitor.Exit(q);
  155.  
  156.                fs.Close();
  157.                fs.Dispose();
  158.            }
  159.  
  160.            protected virtual void OnFileCopyNotification(FileCopyEventArgs e)
  161.            {
  162.                FileCopyEvent(this, e);
  163.            }
  164.        }
  165.  
  166.        private class FileConsumer
  167.        {
  168.            FileStream fd;
  169.            string filename;
  170.            Queue q;
  171.            double TotalLength;
  172.            int oldPercent = 0, percent = 0;
  173.  
  174.            public event FileCopyEventHandler FileCopyEvent;
  175.  
  176.            public FileConsumer(Queue queue, string destination, double sourcelength)
  177.            {
  178.                q = queue;
  179.                filename = destination;
  180.                TotalLength = sourcelength;
  181.            }
  182.  
  183.            public void ThreadRun()
  184.            {
  185.                fd = File.Create(filename);
  186.                object o;
  187.                byte[] buffer;
  188.                bool Continue = true;
  189.  
  190.                while (Continue)
  191.                {
  192.                    Monitor.Enter(q);
  193.  
  194.                    if (q.Count == 0)
  195.                    {
  196.                        Monitor.Wait(q);
  197.                    }
  198.  
  199.                    o = q.Dequeue();
  200.                    buffer = (byte[])o;
  201.  
  202.                    if (buffer.Length == 0)
  203.                    {
  204.                        Continue = false;
  205.                    }
  206.                    
  207.                    Monitor.Pulse(q);
  208.                    Monitor.Exit(q);
  209.  
  210.                    if (Continue)
  211.                    {
  212.                        fd.Write(buffer, 0, buffer.Length);
  213.  
  214.                        percent = (int)((double)fd.Position / TotalLength * 100);
  215.                        if (percent != oldPercent)
  216.                        {
  217.                            OnFileCopyNotification(new FileCopyEventArgs(percent));
  218.                            oldPercent = percent;
  219.                        }
  220.                    }
  221.                }
  222.  
  223.                fd.Close();
  224.                fd.Dispose();
  225.            }
  226.  
  227.            protected virtual void OnFileCopyNotification(FileCopyEventArgs e)
  228.            {
  229.                FileCopyEvent(this, e);
  230.            }
  231.        }
  232.    }
  233. }


Message édité par MagicBuzz le 31-12-2009 à 15:29:07
Reply

Marsh Posté le 31-12-2009 à 14:11:16    

:ouch: il devient urgent qu'on te trouve un vrai travail a toi.

Reply

Marsh Posté le 31-12-2009 à 14:18:45    

En tout cas, à quelques optimisations près, maintenant t'as sous la main un FileCopy asynchrone qui donne des nouvelles en fonction de son avancement à la fois sur le fichier source et le fichier destination :D

 

C'est joli, je m'en lasse pas :D

 

http://img24.imageshack.us/img24/456/filecopy.png


Message édité par MagicBuzz le 31-12-2009 à 14:29:51
Reply

Marsh Posté le 31-12-2009 à 14:18:45   

Reply

Marsh Posté le 31-12-2009 à 15:30:23    

T'ain je sais pas ce que c'est que la brouette que j'utilise, mais le disque dur est vachement moins rapides que le serveur de fichier... Sur un réseau encombré à 100 Mb, ça fait peur :o

Reply

Marsh Posté le 04-01-2010 à 11:31:38    

Ca dépend aussi surtout de l'outil que tu utilises pour effectuer la copie.
En effet, l'Explorateur Windows est une daube infâme en ce qui concerne les performances. Outre la fragmentation (ça, j'y crois pas, car même quand c'est pas fragmenté ça déconne), il passe son temps à accéder à la table d'allocation, et visiblement à y flusher systématiquement toutes les données qu'il écrit dedans. Du coup tout le cache système est inutile. Ensuite, ça semble pas optimisé du tout.
 
Y'a des tas de logiciels en remplacement de l'explorateur Windows qui sont bien plus rapide pour faire ce genre d'actions. (surtout sous Vista, où c'est vraiment devenu catastrophique)

Reply

Marsh Posté le 04-01-2010 à 14:15:53    

Genre SuperCopier :D


---------------
Hobby eien /人◕ ‿‿ ◕人\
Reply

Marsh Posté le 04-01-2010 à 14:40:50    

(faut noter aussi qu'il faut comparer ce qui est comparable)
 
En effet, je veux pas spécialement me faire l'avocat du diable, mais quand sous Windows Vista tu copies un document, il est réindexé dans la foulé afin que la recherche rapide soit à jour. Eventuellement, le superfetch aussi va se mettre à jour dans la foulée, etc.
Sous Linux, de base, t'as ni superfetch ni indexation qui viennent foutre le bordel et tout ralentir ;)

Reply

Marsh Posté le 04-01-2010 à 15:23:29    

l'indexation ca se désactive hein...


---------------
Hobby eien /人◕ ‿‿ ◕人\
Reply

Marsh Posté le 04-01-2010 à 15:29:01    

Tout comme on peut en mettre une en place sur un serveur Linux :spamafote:
 
Je dis juste que quand Linux travaille dans le système de fichiers, il fait (en générale) juste ce qu'on lui demande.
 
Windows, lui, il fait généralement tout un tas de trucs et accessoirement, quand il lui reste du temps, il copie le fichier.
 
Mais y'a pas moyen d'en tirer des conclusions genre "le système de fichier de windows est moins performant" ou "linux gère mieux la copie des fichiers". C'est juste qu'ils ne font pas la même chose.
Après, je dis pas qui a raison ou non hein ;) C'est débile que copier un répertoire de 1000 fichiers de 1 Ko mette autant de temps que copier 100 Go dans un seul fichier, je suis parfaitement d'accord.

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed