Pergunta

O que é a melhor maneira de copiar o conteúdo de um fluxo para outro? Existe um método utilitário padrão para isso?

Foi útil?

Solução

De .NET 4.5 em diante, não é o método Stream.CopyToAsync

input.CopyToAsync(output);

Isso irá retornar um Task que pode ser continuado em quando concluído, assim:

await input.CopyToAsync(output)

// Code from here on will be run in a continuation.

Note que, dependendo de onde a chamada para CopyToAsync é feita, o código que se segue pode ou não continuar no mesmo segmento que o chamou.

O SynchronizationContext que foi capturado quando chamado < a href = "http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx" rel = "noreferrer"> await vai determinar o que enfiar a continuação será executada.

Além disso, esta chamada (e esta é uma implementação detalhe sujeita a mudança) ainda sequências de leituras e gravações (ele simplesmente não desperdiçar um tópicos bloqueio on I / O de conclusão).

noreferrer De .NET 4.0 em diante, há é a método Stream.CopyTo

input.CopyTo(output);

Para NET 3.5 e antes

Não há nada cozido no quadro para ajudar com isso; você tem que copiar o conteúdo manualmente, assim:

public static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[32768];
    int read;
    while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
    {
        output.Write (buffer, 0, read);
    }
}

Nota 1: Este método permitirá que você relatar o progresso (x bytes lido até agora ...)
Nota 2: Por que usar um tamanho de buffer fixo e não input.Length? Porque esse comprimento pode não estar disponível! Do docs :

Se uma classe derivada de fluxo não suporta busca, chamadas para Comprimento, SetLength, posição e buscar lançar um NotSupportedException.

Outras dicas

MemoryStream tem .WriteTo (outStream);

e .NET 4.0 tem .CopyTo em objeto de fluxo normal.

.NET 4.0:

instream.CopyTo(outstream);

Eu uso os seguintes métodos de extensão. Eles têm otimizado sobrecargas para quando um fluxo é um MemoryStream.

    public static void CopyTo(this Stream src, Stream dest)
    {
        int size = (src.CanSeek) ? Math.Min((int)(src.Length - src.Position), 0x2000) : 0x2000;
        byte[] buffer = new byte[size];
        int n;
        do
        {
            n = src.Read(buffer, 0, buffer.Length);
            dest.Write(buffer, 0, n);
        } while (n != 0);           
    }

    public static void CopyTo(this MemoryStream src, Stream dest)
    {
        dest.Write(src.GetBuffer(), (int)src.Position, (int)(src.Length - src.Position));
    }

    public static void CopyTo(this Stream src, MemoryStream dest)
    {
        if (src.CanSeek)
        {
            int pos = (int)dest.Position;
            int length = (int)(src.Length - src.Position) + pos;
            dest.SetLength(length); 

            while(pos < length)                
                pos += src.Read(dest.GetBuffer(), pos, length - pos);
        }
        else
            src.CopyTo((Stream)dest);
    }

As perguntas básicas que as implementações Diferenciar de "CopyStream" são:

  • tamanho do buffer de leitura
  • tamanho das gravações
  • podemos usar mais de uma thread (escrevendo enquanto estamos lendo).

As respostas a estas perguntas resultar em muito diferentes implementações de CopyStream e dependem do tipo de correntes que você tem eo que você está tentando otimizar. O "melhor" implementação sequer precisa saber o hardware específico as correntes estavam lendo e escrevendo.

Não é verdade, uma forma menos pesada de fazer uma cópia córrego. Tome nota no entanto, que isso implica que você pode armazenar o arquivo inteiro na memória. Não tente usar este se você estiver trabalhando com arquivos que vão para as centenas de megabytes ou mais, sem precaução.

public static void CopyStream(Stream input, Stream output)
{
  using (StreamReader reader = new StreamReader(input))
  using (StreamWriter writer = new StreamWriter(output))
  {
    writer.Write(reader.ReadToEnd());
  }
}

NOTA:. Também pode haver algumas questões relativas codificações de dados e de caracteres binários

.NET Framework 4 introduzir novo método "CopyTo" de Fluxo Classe de System.IO namespace. Usando esse método, podemos copiar um fluxo para outro de classe diferente fluxo.

Aqui está um exemplo para isso.

    FileStream objFileStream = File.Open(Server.MapPath("TextFile.txt"), FileMode.Open);
    Response.Write(string.Format("FileStream Content length: {0}", objFileStream.Length.ToString()));

    MemoryStream objMemoryStream = new MemoryStream();

    // Copy File Stream to Memory Stream using CopyTo method
    objFileStream.CopyTo(objMemoryStream);
    Response.Write("<br/><br/>");
    Response.Write(string.Format("MemoryStream Content length: {0}", objMemoryStream.Length.ToString()));
    Response.Write("<br/><br/>");

Infelizmente, não há uma solução muito simples. Você pode tentar algo assim:

Stream s1, s2;
byte[] buffer = new byte[4096];
int bytesRead = 0;
while (bytesRead = s1.Read(buffer, 0, buffer.Length) > 0) s2.Write(buffer, 0, bytesRead);
s1.Close(); s2.Close();

Mas o problema com que aquele diferente implementação da classe de fluxo pode se comportar de forma diferente se não há nada para ler. Um fluxo de leitura de um arquivo de um disco rígido local, provavelmente irá bloquear até que o operaition leitura leu dados suficientes do disco para preencher a reserva e retornar apenas menos dados se atingir o final do arquivo. Por outro lado, um fluxo de leitura a partir da rede pode retornar menos dados mesmo que não haja mais dados deixaram de ser recebido.

Sempre verifique a documentação da classe fluxo específico que você está usando antes de usar uma solução genérica.

Pode haver uma maneira de fazer isso de forma mais eficiente, dependendo do tipo de fluxo que você está trabalhando. Se você pode converter um ou ambos os fluxos para um MemoryStream, você pode usar o método GetBuffer para trabalhar diretamente com uma matriz de bytes que representa seus dados. Isso permite usar métodos como Array.CopyTo, que abstrair todas as questões levantadas pela fryguybob. Você só pode confiar em .NET para saber a melhor maneira de copiar os dados.

Se você quer um procdure para copiar um fluxo para outro o que nick postou é bom, mas está faltando o reset posição, deve ser

public static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[32768];
    long TempPos = input.Position;
    while (true)    
    {
        int read = input.Read (buffer, 0, buffer.Length);
        if (read <= 0)
            return;
        output.Write (buffer, 0, read);
    }
    input.Position = TempPos;// or you make Position = 0 to set it at the start
}

mas se for em tempo de execução não usando um procedimento que shpuld usar fluxo de memória

Stream output = new MemoryStream();
byte[] buffer = new byte[32768]; // or you specify the size you want of your buffer
long TempPos = input.Position;
while (true)    
{
    int read = input.Read (buffer, 0, buffer.Length);
    if (read <= 0)
        return;
    output.Write (buffer, 0, read);
 }
    input.Position = TempPos;// or you make Position = 0 to set it at the start

Uma vez que nenhuma das respostas ter coberto uma maneira assíncrona de copiar a partir de um fluxo para outro, aqui é um padrão que eu tenho usado com sucesso em um aplicativo de encaminhamento de porta para copiar dados de um fluxo de rede para outro. Ela carece de tratamento de exceção para enfatizar o padrão.

const int BUFFER_SIZE = 4096;

static byte[] bufferForRead = new byte[BUFFER_SIZE];
static byte[] bufferForWrite = new byte[BUFFER_SIZE];

static Stream sourceStream = new MemoryStream();
static Stream destinationStream = new MemoryStream();

static void Main(string[] args)
{
    // Initial read from source stream
    sourceStream.BeginRead(bufferForRead, 0, BUFFER_SIZE, BeginReadCallback, null);
}

private static void BeginReadCallback(IAsyncResult asyncRes)
{
    // Finish reading from source stream
    int bytesRead = sourceStream.EndRead(asyncRes);
    // Make a copy of the buffer as we'll start another read immediately
    Array.Copy(bufferForRead, 0, bufferForWrite, 0, bytesRead);
    // Write copied buffer to destination stream
    destinationStream.BeginWrite(bufferForWrite, 0, bytesRead, BeginWriteCallback, null);
    // Start the next read (looks like async recursion I guess)
    sourceStream.BeginRead(bufferForRead, 0, BUFFER_SIZE, BeginReadCallback, null);
}

private static void BeginWriteCallback(IAsyncResult asyncRes)
{
    // Finish writing to destination stream
    destinationStream.EndWrite(asyncRes);
}

Para NET 3.5 e antes tentativa:

MemoryStream1.WriteTo(MemoryStream2);

Fácil e seguro - fazer novo fluxo de fonte original:

    MemoryStream source = new MemoryStream(byteArray);
    MemoryStream copy = new MemoryStream(byteArray);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top