IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Progression d'une copie de fichier en binaire ?


Sujet :

C#

  1. #1
    Membre du Club
    Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2006
    Messages
    256
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 256
    Points : 62
    Points
    62
    Par défaut Progression d'une copie de fichier en binaire ?
    Bonjour

    J'ai un petit souci, je cherche à afficher la progression lors de la copie d'un fichier avec cette procédure :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
     
    public virtual void CopyStreams2(string filesrc, string filedest, CopyProgressHandler handler)
            {
                int i;
                FileStream fin, fout;
                long done = 0, total = 0;
     
                try
                {
                    // open input file 
                    try
                    {
                        fin = new FileStream(filesrc, FileMode.Open);
                        total = fin.Length;
                    }
                    catch (FileNotFoundException exc)
                    {
                        Console.WriteLine(exc.Message + "\nInput File Not Found");
                        return;
                    }
     
                    // open output file 
                    try
                    {
                        fout = new FileStream(filedest, FileMode.Create);
                    }
                    catch (IOException exc)
                    {
                        Console.WriteLine(exc.Message + "\nError Opening Output File");
                        return;
                    }
                }
                catch (IndexOutOfRangeException exc)
                {
                    Console.WriteLine(exc.Message + "\nUsage: CopyFile From To");
                    return;
                }
     
                // Copy File 
                try
                {
                    do
                    {
                        i = fin.ReadByte();
                        if (i != -1)
                        {
                            fout.WriteByte((byte)i);
                            if (handler != null)
                            {
                                done = fin.Position;
                                if ((done % 8) == 0) // taille d'un octet on incrémente la progression
                                    handler(total, done, total - done, (double)done / (double)total);
                            }
                        }
     
                    } while (i != -1);
                }
                catch (IOException exc)
                {
                    Console.WriteLine(exc.Message + "File Error");
                }
     
                fin.Close();
                fout.Close();
            }
    Comment puis je afficher la progression de copie via une ProgressBar de valeur MAX 100 ? Comme vous pouvez le voir, j'ai commencé à faire quelque chose mais la progression s'effectue mal.
    http://stef-le-buffle.labrute.com

  2. #2
    Membre éclairé Avatar de ZaaN
    Inscrit en
    Novembre 2005
    Messages
    819
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 819
    Points : 661
    Points
    661
    Par défaut
    Citation Envoyé par Shypster Voir le message
    j'ai commencé à faire quelque chose mais la progression s'effectue mal.
    C'est a dire ? tu as des retards ? des sauts de progression ?


    PS: pense a fermer tes Streams dans le finaly de tes bloc try-catch ;-)
    Pour les details, cherche tout seul !

  3. #3
    Membre du Club
    Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2006
    Messages
    256
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 256
    Points : 62
    Points
    62
    Par défaut
    La progression est trés en retard par rapport à la copie.
    http://stef-le-buffle.labrute.com

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par ZaaN Voir le message
    PS: pense a fermer tes Streams dans le finaly de tes bloc try-catch ;-)
    Ou mieux, utilise des blocs using

    Citation Envoyé par Shypster Voir le message
    La progression est trés en retard par rapport à la copie.
    Normal, si tu utilises le thread d'interface pour faire la copie, l'interface ne se met plus à jour... Il faut effectuer la copie dans un thread séparé (ou dans un BackgroundWorker, plus simple)

    D'autre part, copier les fichiers octet par octet est très inefficace... utilise plutôt un buffer de 4 ou 8 Ko

  5. #5
    Membre du Club
    Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2006
    Messages
    256
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 256
    Points : 62
    Points
    62
    Par défaut
    voilà d'aprés vos remarques, mon code modifié :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public virtual void CopyStreams(Stream a, Stream b, CopyProgressHandler handler)
    {
    int bufSize = 4 * 1024; /* 4 Ko */
    byte[] buffer = new byte[bufSize];
    long done = 0;
    long total = a.Length;
    int tmp = 0;
    while ((tmp = a.read(buffer, 0, bufSize)) > 0)
    {
    b.Write(buffer, 0, tmp);
    if (handler != null)
    {
    done += tmp;
    handler(total, done, total - done, (double)done / (double)total);
    }
    }
    }
    Qu'en pensez - vous, la progression se passe bien mais par rapport à un corbian backup, mon application met env 10 min de plus pour copier un même fichier. Puis-je optimiser le code pour qu'il s'exécute plus rapidement ?
    http://stef-le-buffle.labrute.com

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Tu pourrais essayer de changer la taille du buffer en cherchant la taille optimale. A part ça je vois pas trop comment tu peux améliorer...
    Les 10 minutes d'écart, c'est sur un fichier de quelle taille ? la copie dure combien au total ?

  7. #7
    Membre du Club
    Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2006
    Messages
    256
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 256
    Points : 62
    Points
    62
    Par défaut
    La copie se fait sur une image ISO de 3,7 go.
    Mon programme met + ou - 20 min
    Corbian backup met + ou - 10 min

    A mon avis, la taille du buffer de 64 ko est en cause mais je ne vois pas vraiment comment je peux trouver la valeur optimale pour mon buffer.

    EDIT : avec 8 ko de buffer, la copie ne met pas 20 min mais + ou - 1 h.
    http://stef-le-buffle.labrute.com

  8. #8
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par Shypster Voir le message
    EDIT : avec 8 ko de buffer, la copie ne met pas 20 min mais + ou - 1 h.
    Aie... ben je sais pas, essaie avec un buffer plus petit alors. Intuitivement, j'aurais dit que la taille optimale était 4ko, vu que c'est la taille de bloc par défaut en NTFS, mais apparemment ce n'est pas le cas... D'ailleurs ça dépend sans doute du filesystem utilisé.

  9. #9
    Membre habitué
    Profil pro
    Inscrit en
    Août 2002
    Messages
    104
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 104
    Points : 128
    Points
    128
    Par défaut
    Bonjour,

    J'ai bien une proposition "non testée" ... il pourrait-être intéressant de faire des lectures asynchrones ({Begin, End}Read).
    Cela implique d'utiliser 2 buffer (travail et chargement) :
    - remplir le premier buffer (de travail) en mode synchrone (appeler simplement le Read classique )
    - début de la boucle
    - Lancer le remplissage du 2ème buffer (de chargement) avec BeginRead
    - pendant ce temps écrire en synchrone (méthode Write) le 1er buffer (de travail)
    - mettre à jour la progression
    - attendre la fin du remplissage du 2ème buffer (de chargement) : EndRead
    - inverser les références (pour mettre le buffer rempli dans la référence du buffer de travail et "l'ancien" buffer de travail dans la référence du buffer de chargement)
    - boucler si la fin du fichier n'est pas atteinte.

    Enjoy

  10. #10
    Membre du Club
    Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2006
    Messages
    256
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 256
    Points : 62
    Points
    62
    Par défaut
    La procédure de copie est déjà dans un thread séparé.
    http://stef-le-buffle.labrute.com

  11. #11
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    C'est pas le traitement du handler de progression qui prend du temps ? Est-ce que ça marche mieux si tu passes un handler null ?

  12. #12
    Membre du Club
    Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2006
    Messages
    256
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 256
    Points : 62
    Points
    62
    Par défaut
    J'ai testé avec le handler nul et aussi sans afficher aucune progression et j'obtiens le même résultat.

    Je ne comprends vraiment pas ce qui se passe.
    Est-ce que le fait d'avoir mon traitement dans la classe de mon formulaire windows peut jouer même si le code est bien exécuté dans un thread à part ?
    http://stef-le-buffle.labrute.com

  13. #13
    Membre habitué
    Profil pro
    Inscrit en
    Août 2002
    Messages
    104
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 104
    Points : 128
    Points
    128
    Par défaut
    Citation Envoyé par Shypster Voir le message
    La procédure de copie est déjà dans un thread séparé.
    Je ne doute pas que la copie soit dans un thread séparé, mais je parle d'effectuer une demande de lecture en parallèle de l'action d'écriture.
    D'où la notion de lecture "asynchrone".

    Sans diviser le temps par 2, cela permet de gagner du temps ... Reste à quantifier ce gain.

    Pour moi : Temps de lecture asynchrone + temps d'écriture synchrone <= temps de lecture synchrone + temps d'écriture synchrone.

    Citation Envoyé par Shypster
    Est-ce que le fait d'avoir mon traitement dans la classe de mon formulaire windows peut jouer même si le code est bien exécuté dans un thread à part ?
    Pour moi ça n'a pas d'influence.

  14. #14
    Membre du Club
    Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2006
    Messages
    256
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 256
    Points : 62
    Points
    62
    Par défaut
    Tu aurais du code pour illustrer ta proposition ?
    Merci.
    http://stef-le-buffle.labrute.com

  15. #15
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par cboun94 Voir le message
    je parle d'effectuer une demande de lecture en parallèle de l'action d'écriture.
    D'où la notion de lecture "asynchrone".

    Sans diviser le temps par 2, cela permet de gagner du temps ... Reste à quantifier ce gain.
    Effectivement... ce sera particulièrement efficace si les disques physiques d'origine et de destination sont différents. Si c'est le même disque, l'intérêt est plus limité, vu que la tête de lecture ne peut pas en même temps lire à un endroit et écrire à un autre...

  16. #16
    Membre habitué
    Profil pro
    Inscrit en
    Août 2002
    Messages
    104
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 104
    Points : 128
    Points
    128
    Par défaut
    Tout le pseudo code est là ...

    Citation Envoyé par cboun94 Voir le message
    Cela implique d'utiliser 2 buffer (travail et chargement) :
    - remplir le premier buffer (de travail) en mode synchrone (appeler simplement le Read classique )
    - début de la boucle
    - Lancer le remplissage du 2ème buffer (de chargement) avec BeginRead
    - pendant ce temps écrire en synchrone (méthode Write) le 1er buffer (de travail)
    - mettre à jour la progression
    - attendre la fin du remplissage du 2ème buffer (de chargement) : EndRead
    - inverser les références (pour mettre le buffer rempli dans la référence du buffer de travail et "l'ancien" buffer de travail dans la référence du buffer de chargement)
    - boucler si la fin du fichier n'est pas atteinte.
    et comme indiqué précédemment, ce n'est pas une solution testée, donc pas de code existant, mais à partir de ton code déjà posté, il n'y a pas plus 10 lignes à ajouter ... courage

  17. #17
    Membre du Club
    Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2006
    Messages
    256
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 256
    Points : 62
    Points
    62
    Par défaut
    J'ai essayé de faire quelque chose avec le pseudo code que tu m'as fourni et mon code existant, le voici :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
    public virtual void CopyStreams2(Stream a, Stream b, CopyProgressHandler handler)
            {
                int bufSize = 64 * 1024; /* 64 Ko */
                byte[] WorkBuffer = new byte[bufSize], LoadBuffer = new byte[bufSize], TmpBuffer = new byte[bufSize];
                long done = 0;
                long total = a.Length;
                int tmp = 0;
                double percent = 0;
     
                tmp = a.Read(WorkBuffer, 0, bufSize);
                while (a.Position > 0)
                {
                    a.BeginRead(LoadBuffer, 0, bufSize, null, "Chargement");
                    b.Write(WorkBuffer, 0, tmp);
                    done += tmp;
                    percent = (double)done / (double)total;
                    ProgressFile = Convert.ToInt16(Math.Round(percent * 100, 0));
                    a.EndRead(null);
                    TmpBuffer = WorkBuffer;
                    WorkBuffer = LoadBuffer; LoadBuffer = TmpBuffer;
                    // if (handler != null)
                    // {
                    // done += tmp;
                    // handler(total, done, total - done, (double)done / (double)total);
                    // }
                }
            }
    Qu'en penses-tu ?
    http://stef-le-buffle.labrute.com

  18. #18
    Membre du Club
    Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2006
    Messages
    256
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 256
    Points : 62
    Points
    62
    Par défaut
    Citation Envoyé par tomlev Voir le message
    Effectivement... ce sera particulièrement efficace si les disques physiques d'origine et de destination sont différents. Si c'est le même disque, l'intérêt est plus limité, vu que la tête de lecture ne peut pas en même temps lire à un endroit et écrire à un autre...
    Dans mon cas, les disques physiques sont bien différents.
    http://stef-le-buffle.labrute.com

  19. #19
    Membre du Club
    Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2006
    Messages
    256
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 256
    Points : 62
    Points
    62
    Par défaut
    J'ai rectifié mon code, j'ai testé la copie et effectivement ta méthode est très efficace (temps / 2 voir +). Je vous met le code pour voir si vous avez encore des optimisations à me proposer.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    public virtual void CopyStreams2(Stream a, Stream b, CopyProgressHandler handler)
            {
                int bufSize = 4 * 1024; /* 64 Ko */
                byte[] WorkBuffer = new byte[bufSize], LoadBuffer = new byte[bufSize], TmpBuffer = new byte[bufSize];
                long done = 0, total = a.Length;
                int tmp = 0;
                double percent = 0;
                IAsyncResult result;
     
                int posit = a.Read(WorkBuffer, 0, bufSize);
                while (done < total)
                {
                    result = a.BeginRead(LoadBuffer, 0, bufSize,
                                                     new AsyncCallback(ReadCallBack),
                                                     a);  
                    b.Write(WorkBuffer, 0, tmp);
                    done += bufSize;
                    percent = (double)done / (double)total;
                    ProgressFile = Convert.ToInt16(Math.Round(percent * 100, 0));
                    a.EndRead(result);
                    TmpBuffer = WorkBuffer;
                    WorkBuffer = LoadBuffer; LoadBuffer = TmpBuffer;
                }
            }
     
            private void ReadCallBack(IAsyncResult ar)
            {
     
     
            }
    En tout cas un GRAND merci à tous ceux qui m'ont aidé.
    http://stef-le-buffle.labrute.com

  20. #20
    Membre habitué
    Profil pro
    Inscrit en
    Août 2002
    Messages
    104
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Août 2002
    Messages : 104
    Points : 128
    Points
    128
    Par défaut
    Hello,

    J'image que ça doit effectivement aller très vite ... tu écris le nombre de byte en fonction de ta variable "tmp" qui pour moi reste à 0 ...

    je te propose les modifications suivantes :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public virtual void CopyStreams2(Stream a, Stream b, CopyProgressHandler handler)
            {
                int bufSize = 4 * 1024; /* 64 Ko */
                byte[] WorkBuffer = new byte[bufSize], LoadBuffer = new byte[bufSize];
                byte[] TmpBuffer = new byte[] /* pas besoin d'avoir un tableau alloué, juste la référence*/;
                long done = 0, total = a.Length;
                //int tmp = 0; // plus utilisé
                double percent = 0;
                IAsyncResult result;
     
                //Attention : le nom de la variable posit laisse entendre qu'il s'agit de la position,  et non pas du nombre de byte lu 
                int posit = a.Read(WorkBuffer, 0, bufSize);
                while (done < total)
                //Attention, le vrai test de fin de lecture est while (posit > 0)
                {
                    result = a.BeginRead(LoadBuffer, 0, bufSize,
                                                     null, //appeler une méthode pour ne rien faire n'est pas très intéressant, ou alors tu testes le temps de réponse pour avoir des stats
                                                     a);  
                    b.Write(WorkBuffer, 0, posit);
                    done += bufSize;
                    percent = (double)done / (double)total;
                    ProgressFile = Convert.ToInt16(Math.Round(percent * 100, 0));
                    posit = a.EndRead(result);
                    TmpBuffer = WorkBuffer;
                    WorkBuffer = LoadBuffer;
                    LoadBuffer = TmpBuffer;
                }
            }
    Par curiosité, peux-tu nous dire quelle est la différence de temps observée ?

    Tu annonçais 20 min avec la version synchrone, quel est le temps observé par rapport aux 20 min précédentes ?

    Merci et de rien

Discussions similaires

  1. Progression d'une copie d'un fichier
    Par Seth77 dans le forum C#
    Réponses: 16
    Dernier message: 25/09/2012, 16h26
  2. Réponses: 9
    Dernier message: 11/05/2012, 10h40
  3. Réponses: 2
    Dernier message: 22/03/2007, 13h25
  4. Problème pour faire une copie de fichier.
    Par damien99 dans le forum C++
    Réponses: 1
    Dernier message: 12/02/2006, 16h37
  5. Réponses: 3
    Dernier message: 19/10/2005, 15h58

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo