Question

J'ai une application d'organisateur de fichiers sur lesquelles je travaille. Les fichiers sont régulièrement de 500 Mo à 2 Go. Tout fonctionne bien, mais il est extrêmement ennuyeux que l'application "cesse de répondre". Ce que j'aimerais faire, c'est un octet par octet ou meg par Meg Copy avec une application.DoEvents () là-dedans après chaque action de lecture / écriture. Quelque chose dans ces lignes, je ne sais pas quelles seraient les classes réelles à utiliser, donc je vais faire des trucs :)

private void CopyFile(string inFilename, string outFilename)
{
    FileReader inReader(inFilename);
    FileWriter outWriter(outFilename, FileMode.OpenOrCreate);

    byte theByte;
    while (theByte = inReader.ReadByte())
    {
        outWriter.WriteByte(theByte, WriteMode.Append);
        UpdateProgressBar();
        Application.DoEvents();
    }

    inReader.CloseFile();
    outWriter.CloseFile();
}

Je sais que cela semble que ce devrait être une chose simple à faire, mais pour la vie de moi, je n'arrive pas à trouver un exemple pour le faire sans utiliser d'appels API directs ou autre. Je dois manquer quelque chose ici, donc si quelqu'un pouvait me mettre sur la bonne piste ici, je l'apprécierais vraiment.

Merci d'avance!

Était-ce utile?

La solution

Vous devez utiliser un Fondateur de fond sur votre formulaire pour faire la copie. Il permettra aux copies de fichiers d'être effectuées sur un thread séparé et laissera votre interface utilisateur réactive. Il y a une complexité supplémentaire, mais le fond de fond s'occupe d'une grande partie de la plomberie pour vous. Mais, il y a beaucoup de exemples de que veux-tu faire.

Autres conseils

Vous devez utiliser un BackgroundworkerThread pour y parvenir. Voici un très bon exemple de la façon de procéder: Copier le fichier à l'aide de threads des travailleurs en arrière-plan

Je voudrais utiliser le CopyFileEx fonction. Si un analogue à cette fonction n'existe pas dans la bibliothèque de framework gérée, alors Google pour l'utiliser quand même: peut-être un article comme http://www.thetechscene.com/2008/09/copyfileex-with-progress-callback-in-c-using-pinvoke/

Ma raison de vouloir utiliser CopyFileEx est que je suppose qu'il est implémenté dans le noyau O / S, les données étant copier d'un fichier à un autre dans le pilote du système de fichiers, sans utiliser la mémoire utilisateur (sans parler de la mémoire gérée).

Threading.threadpool.QueueUserWorkItem devrait vous faire passer le bien.

Une approche consiste à effectuer l'opération de copie dans un thread séparé. Votre application principale continuera à s'exécuter normalement pendant que le thread effectue le travail de copie du fichier. Vous souhaiterez bien sûr ajouter une communication entre le thread et l'application principale afin que vous puissiez mettre à jour votre barre de progression ou votre mécanisme de rétroaction similaire.

Si vous préférez ne pas gérer plusieurs threads, une autre approche consiste à créer une classe qui contient des variables d'état pour l'opération de copie et une fonction membre qui s'appelle périodiquement à partir de votre application principale afin de copier un certain nombre d'octets à chaque fois qu'il s'appelle .

En plus d'exécuter un thread d'arrière-plan, vous devez noter que vous copiez 512m-2g de données un octet à la fois. Cela se traduira par jusqu'à 2 MILLIARD Appels à ReadByte et WriteByte. J'espère que ces appels se tampon quelque part pour ne pas en faire 2 MILLIARD a réussi à des transitions non gérées, mais même cela s'additionnera sûrement.

La mémoire n'est pas gratuite, mais c'est sûr que diable est bon marché. Allouer un tampon (peut-être 16k-64k) et copier en morceaux. Non, le code n'est pas aussi simple que vous devez gérer un cas de ne pas lire le bloc entier, mais je préfère les invocations de la méthode 2G / 64K sur 2G.

Vous avez deux problèmes ici. La première est que le thread GUI n'est pas réactif lors de la copie de fichiers volumineux. Vous devez utiliser un fil d'arrière-plan pour résoudre cela, comme d'autres l'ont suggéré.

L'autre problème est que votre routine de copie de fichiers actuelle ne prend pas en charge une fonction de rappel de progression. La réponse acceptée à la question ci-dessous a les informations dont vous avez besoin pour rédiger votre propre solution:

Puis-je afficher les progrès de la copie de fichiers à l'aide de fileInfo.copyto () dans .net?

Edit: je viens de trouver ça Classe de wrapper pour CopyFileEx. Je l'ai testé et ça marche très bien!

using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;

namespace FileCopyTest {
    public sealed class FileRoutines {
        public static void CopyFile(FileInfo source, FileInfo destination) {
            CopyFile(source, destination, CopyFileOptions.None);
        }

        public static void CopyFile(FileInfo source, FileInfo destination,
            CopyFileOptions options) {
            CopyFile(source, destination, options, null);
        }

        public static void CopyFile(FileInfo source, FileInfo destination,
            CopyFileOptions options, CopyFileCallback callback) {
            CopyFile(source, destination, options, callback, null);
        }

        public static void CopyFile(FileInfo source, FileInfo destination,
            CopyFileOptions options, CopyFileCallback callback, object state) {
            if (source == null) throw new ArgumentNullException("source");
            if (destination == null)
                throw new ArgumentNullException("destination");
            if ((options & ~CopyFileOptions.All) != 0)
                throw new ArgumentOutOfRangeException("options");

            new FileIOPermission(
                FileIOPermissionAccess.Read, source.FullName).Demand();
            new FileIOPermission(
                FileIOPermissionAccess.Write, destination.FullName).Demand();

            CopyProgressRoutine cpr = callback == null ?
                null : new CopyProgressRoutine(new CopyProgressData(
                    source, destination, callback, state).CallbackHandler);

            bool cancel = false;
            if (!CopyFileEx(source.FullName, destination.FullName, cpr,
                IntPtr.Zero, ref cancel, (int)options)) {
                throw new IOException(new Win32Exception().Message);
            }
        }

        private class CopyProgressData {
            private FileInfo _source = null;
            private FileInfo _destination = null;
            private CopyFileCallback _callback = null;
            private object _state = null;

            public CopyProgressData(FileInfo source, FileInfo destination,
                CopyFileCallback callback, object state) {
                _source = source;
                _destination = destination;
                _callback = callback;
                _state = state;
            }

            public int CallbackHandler(
                long totalFileSize, long totalBytesTransferred,
                long streamSize, long streamBytesTransferred,
                int streamNumber, int callbackReason,
                IntPtr sourceFile, IntPtr destinationFile, IntPtr data) {
                return (int)_callback(_source, _destination, _state,
                    totalFileSize, totalBytesTransferred);
            }
        }

        private delegate int CopyProgressRoutine(
            long totalFileSize, long TotalBytesTransferred, long streamSize,
            long streamBytesTransferred, int streamNumber, int callbackReason,
            IntPtr sourceFile, IntPtr destinationFile, IntPtr data);

        [SuppressUnmanagedCodeSecurity]
        [DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool CopyFileEx(
            string lpExistingFileName, string lpNewFileName,
            CopyProgressRoutine lpProgressRoutine,
            IntPtr lpData, ref bool pbCancel, int dwCopyFlags);
    }

    public delegate CopyFileCallbackAction CopyFileCallback(
        FileInfo source, FileInfo destination, object state,
        long totalFileSize, long totalBytesTransferred);

    public enum CopyFileCallbackAction {
        Continue = 0,
        Cancel = 1,
        Stop = 2,
        Quiet = 3
    }

    [Flags]
    public enum CopyFileOptions {
        None = 0x0,
        FailIfDestinationExists = 0x1,
        Restartable = 0x2,
        AllowDecryptedDestination = 0x8,
        All = FailIfDestinationExists | Restartable | AllowDecryptedDestination
    }
}

Ce que vous voulez vraiment faire, c'est avoir une application multi-thread et faire la copie de fichier dans un thread d'arrière-plan, de cette façon, votre thread principal ne sera pas lié.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top