¿Cómo copio el contenido de una transmisión a otra?


¿Cuál es la mejor manera de copiar el contenido de una secuencia a otra? ¿Hay un método de utilidad estándar para esto?

Author: David Heffernan, 2008-10-23

12 answers

De .NET 4.5, existe la Stream.CopyToAsync método

input.CopyToAsync(output);

Esto devolverá un Task que se puede continuar cuando se completa, así:

await input.CopyToAsync(output)

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

Tenga en cuenta que dependiendo de dónde se realice la llamada a CopyToAsync, el código que sigue puede o no continuar en el mismo hilo que lo llamó.

El SynchronizationContext que fue capturado al llamar await determinará qué hilo se ejecutará la continuación en.

Además, esta llamada (y este es un detalle de implementación sujeto a cambios) todavía secuencias lee y escribe (simplemente no desperdicia un bloqueo de subprocesos en la finalización de E/S).

A partir de. NET 4.0, hay es el Stream.CopyTo método

input.CopyTo(output);

Para. NET 3.5 y antes

No hay nada horneado en el marco para ayudar con esto; tienes que copiar el contenido manualmente, así: {[16]]}

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 le permitirá informar sobre el progreso (x bytes leídos hasta ahora ...)
Nota 2: ¿Por qué usar un tamaño de búfer fijo y no input.Length? Debido a que la longitud puede no estar disponible! De los documentos :

Si una clase derivada de Stream no admite la búsqueda, llama a Length, SetLength, Position y Seek para lanzar una NotSupportedException.

 640
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-24 12:14:53

MemoryStream tiene .WriteTo(outstream);

Y .NET 4.0 tiene.CopyTo en objeto de flujo normal.

. NET 4.0:

instream.CopyTo(outstream);
 61
Author: Joshua,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-19 17:08:01

Utilizo los siguientes métodos de extensión. Han optimizado las sobrecargas para cuando una transmisión es un 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);
    }
 31
Author: Eloff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-10 03:54:43

Las preguntas básicas que diferencian las implementaciones de "CopyStream" son:

  • tamaño del búfer de lectura
  • tamaño de las escrituras
  • Podemos usar más de un hilo (escribir mientras leemos).

Las respuestas a estas preguntas resultan en implementaciones muy diferentes de CopyStream y dependen del tipo de flujos que tenga y lo que esté tratando de optimizar. La "mejor" implementación incluso necesitaría saber qué hardware específico los arroyos estaban leyendo y escribiendo a.

 1
Author: fryguybob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-23 15:29:53

En realidad, hay una forma menos pesada de hacer una copia de flujo. Sin embargo, tenga en cuenta que esto implica que puede almacenar todo el archivo en memoria. No intente usar esto si está trabajando con archivos que van a cientos de megabytes o más, sin precaución.

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: También puede haber algunos problemas relacionados con datos binarios y codificaciones de caracteres.

 1
Author: SmokingRope,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-06 06:52:04

. NET Framework 4 introducir nuevo método "CopyTo" de Clase de flujo de System.IO espacio de nombres. Usando este método podemos copiar un flujo a otro flujo de diferente clase de flujo.

Aquí hay un ejemplo para esto.

    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/>");
 1
Author: Jayesh Sorathia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-27 11:44:17

Desafortunadamente, no hay una solución realmente simple. Puedes probar algo así:

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

Pero el problema con eso es que la implementación diferente de la clase Stream podría comportarse de manera diferente si no hay nada que leer. Un flujo que lee un archivo desde un disco duro local probablemente se bloqueará hasta que la operación de lectura haya leído suficientes datos del disco para llenar el búfer y solo devuelva menos datos si llega al final del archivo. Por otro lado, una lectura de la corriente de la red podría devolver menos datos a pesar de que quedan más datos por recibir.

Compruebe siempre la documentación de la clase de flujo específica que está utilizando antes de utilizar una solución genérica.

 0
Author: Tamas Czinege,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-23 15:26:28

Puede haber una manera de hacer esto de manera más eficiente, dependiendo del tipo de transmisión con la que esté trabajando. Si puede convertir uno o ambos flujos en un MemoryStream, puede usar el método GetBuffer para trabajar directamente con una matriz de bytes que represente sus datos. Esto le permite usar métodos como Array.CopyTo, que abstrae todas las cuestiones planteadas por fryguybob. Solo puede confiar en. NET para conocer la forma óptima de copiar los datos.

 0
Author: Coderer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-23 17:24:29

Si desea que un procdure copie un flujo a otro el que nick publicó está bien, pero le falta el restablecimiento de posición, debe 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
}

Pero si está en tiempo de ejecución no utilizando un procedimiento que shpuld utilizar flujo de memoria

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
 0
Author: Kronass,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-22 16:44:40

Dado que ninguna de las respuestas ha cubierto una forma asíncrona de copiar de un flujo a otro, aquí hay un patrón que he utilizado con éxito en una aplicación de reenvío de puertos para copiar datos de un flujo de red a otro. Carece de manejo de excepciones para enfatizar el patrón.

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);
}
 0
Author: mdonatas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-07 21:05:55

Para. NET 3.5 y antes de intentar:

MemoryStream1.WriteTo(MemoryStream2);
 0
Author: ntiago,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-29 16:52:36

Fácil y seguro-hacer nueva corriente de la fuente original:

    MemoryStream source = new MemoryStream(byteArray);
    MemoryStream copy = new MemoryStream(byteArray);
 0
Author: Graham Laight,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-30 12:47:48