Pergunta

Tenho um arquivo organizador aplicativo que eu estou trabalhando.Os arquivos são regularmente 500MB 2GB.Tudo funciona bem, mas é extremamente irritante que o aplicativo Pára de Responder." O que eu gostaria de fazer é um byte por byte ou por meg meg cópia com algumas aplicações.DoEvents() depois de cada leitura/gravação ação.Algo ao longo dessas linhas, eu não sei o que as classes reais de uso seria então, eu estou indo só para fazer algumas coisas :)

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

Eu sei que isto parece que deve ser uma coisa simples de fazer, mas eu para a vida de mim não consigo encontrar qualquer tipo de exemplo, para fazer isso sem usar chamadas de API diretas ou o que seja.Tenho de estar faltando alguma coisa aqui, então se alguém pudesse me no caminho certo aqui eu realmente aprecio isso.

Obrigado antecipadamente!

Foi útil?

Solução

Você deve usar um BackgroundWorker em seu formulário para fazer a cópia. Ele permitirá que as cópias do arquivo sejam feitas em um encadeamento separado e deixe sua interface do usuário ser responsiva. Há alguma complexidade adicional, mas o trabalho de fundo cuida de muito do encanamento para você. Mas há muitos exemplos do O que você quer fazer.

Outras dicas

Você precisa usar um BackgroundWorkerThread Para fazer isso. Aqui está um bom exemplo de como fazer isso: Copie o arquivo usando threads de trabalhadores em segundo plano

Eu gostaria de usar o CopyFileEx função. Se um análogo a essa função não existe na biblioteca da estrutura gerenciada, então Google como usá -la de qualquer maneira: talvez um artigo como http://www.thetechscene.com/2008/09/copyfileex-with-progress-callback-in-c-using-pinvoke/

Minha razão para querer usar CopyFileEx é que presumo que seja implementado no kernel O/S, com os dados sendo copia de um arquivo para outro dentro do driver do sistema de arquivos, sem usar a memória do usuário (muito menos a memória gerenciada).

Threading.Threadpool.queueUserworkItem deve dar um bom tempo no seu caminho.

Uma abordagem é executar a operação de cópia em um thread separado.Sua principal aplicação irá continuar a funcionar normalmente enquanto o thread executa o trabalho de copiar o arquivo.Claro que você vai querer adicionar a comunicação entre a linha e a principal aplicação de modo que você pode atualizar sua barra de progresso ou similar mecanismo de feedback.

Se você prefere não lidar com vários threads, outra abordagem é criar uma classe que contém variáveis de estado para a operação de cópia e uma função de membro que é chamado periodicamente a partir de seu principal aplicativo para copiar um determinado número de bytes de cada vez que é chamado.

Além de executar um encadeamento em segundo plano, você deve observar que está copiando 512m-2g de dados um byte de cada vez. Isso se traduzirá em até 2 BILHÃO chamadas para Readbyte e WriteByte. Espero que essas chamadas bufferm em algum lugar para que você não faça 2 BILHÃO conseguiu transições não gerenciadas, mas mesmo assim isso certamente aumentará.

A memória não é gratuita, mas tem certeza de que é barato. ALoque um buffer (talvez 16k-64k) e copie em pedaços. Não, o código não é tão simples quanto você precisará lidar com um caso de não ler o bloco inteiro, mas prefiro invocações de método 2G/64K acima de 2G.

Você tem dois problemas aqui. A primeira é que o thread da GUI não responde ao copiar arquivos grandes. Você deve usar um encadeamento de segundo plano para resolver isso, como outros sugeriram.

O outro problema é que sua rotina de cópia de arquivo atual não suporta uma função de retorno de chamada de progresso. A resposta aceita para a pergunta abaixo tem as informações necessárias para escrever sua própria solução:

Posso mostrar o progresso da cópia do arquivo usando o FileInfo.copyTo () no .NET?

Edit: acabei de encontrar isso Classe de wrapper para copyfileEx. Eu testei e funciona muito bem!

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

O que você realmente deseja fazer é ter um aplicativo multithread e fazer a cópia do arquivo em um thread em segundo plano, para que seu thread principal não seja amarrado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top