Question Comment copier le contenu d'un flux vers un autre?


Quel est le meilleur moyen de copier le contenu d'un flux dans un autre? Existe-t-il une méthode d'utilité standard pour cela?


468
2017-10-23 15:16


origine


Réponses:


A partir de .NET 4.5, il y a le Stream.CopyToAsync méthode

input.CopyToAsync(output);

Cela retournera un Task cela peut être continué quand terminé, comme ça:

await input.CopyToAsync(output)

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

Notez que selon où l'appel à CopyToAsync est fait, le code qui suit peut ou ne peut pas continuer sur le même fil qui l'a appelé.

le SynchronizationContext qui a été capturé lors de l'appel await déterminera sur quel thread la suite sera exécutée.

De plus, cet appel (et il s'agit d'un détail d'implémentation sujet à changement) lit et écrit des séquences encore (cela ne gâche pas un thread qui bloque l'achèvement des E / S).

À partir de .NET 4.0, il y a le Stream.CopyTo méthode

input.CopyTo(output);

Pour .NET 3.5 et avant

Il n'y a rien de prévu dans le cadre pour aider à cela; vous devez copier le contenu manuellement, comme ceci:

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

631
2018-06-22 02:41



MemoryStream a .WriteTo (outstream);

et .NET 4.0 a .CopyTo sur l'objet de flux normal.

.NET 4.0:

instream.CopyTo(outstream);

61
2017-08-10 03:54



J'utilise les méthodes d'extension suivantes. Ils ont optimisé les surcharges pour quand un flux est 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
2017-10-23 15:29



Les questions de base qui différencient les implémentations de "CopyStream" sont:

  • taille du tampon de lecture
  • taille des écritures
  • Pouvons-nous utiliser plus d'un fil (écrire pendant que nous lisons).

Les réponses à ces questions entraînent des implémentations très différentes de CopyStream et dépendent du type de flux que vous avez et de ce que vous essayez d'optimiser. La «meilleure» implémentation aurait même besoin de savoir à quel matériel spécifique les flux étaient en train de lire et d'écrire.


1
2017-10-23 15:26



Il y a en fait, une façon moins lourde de faire une copie de flux. Notez toutefois que cela implique que vous pouvez stocker le fichier entier en mémoire. N'essayez pas de l'utiliser si vous travaillez avec des fichiers de plusieurs centaines de mégaoctets ou plus, sans faire attention.

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

Remarque: il peut également y avoir certains problèmes concernant les données binaires et les codages de caractères.


1
2017-10-23 17:24



Malheureusement, il n'y a pas de solution vraiment simple. Vous pouvez essayer quelque chose comme ça:

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

Mais le problème avec cette implémentation différente de la classe Stream pourrait se comporter différemment s'il n'y a rien à lire. Un flux lisant un fichier à partir d'un disque dur local sera probablement bloqué jusqu'à ce que l'opération de lecture ait suffisamment lu le disque pour remplir le tampon et renvoyer moins de données si elle atteint la fin du fichier. D'un autre côté, un flux lu sur le réseau peut renvoyer moins de données, même s'il reste plus de données à recevoir.

Vérifiez toujours la documentation de la classe de flux spécifique que vous utilisez avant d'utiliser une solution générique.


0
2018-03-22 16:44



Il y a peut-être un moyen de le faire plus efficacement, selon le type de flux avec lequel vous travaillez. Si vous pouvez convertir un ou vos deux flux en MemoryStream, vous pouvez utiliser la méthode GetBuffer pour travailler directement avec un tableau d'octets représentant vos données. Cela vous permet d'utiliser des méthodes telles que Array.CopyTo, qui résout tous les problèmes soulevés par fryguybob. Vous pouvez simplement faire confiance à .NET pour connaître la manière optimale de copier les données.


0
2017-11-07 21:05



Si vous voulez une procdure de copier un flux vers d'autres celui que le pseudo posté est correct mais il manque la position reset, il devrait être

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
}

mais si c'est en cours d'exécution ne pas utiliser une procédure que vous utilisez shpuld flux de mémoire

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
2017-07-27 08:42



Comme aucune des réponses n'a couvert une manière asynchrone de copier d'un flux vers un autre, voici un modèle que j'ai utilisé avec succès dans une application de redirection de port pour copier des données d'un flux réseau vers un autre. Il manque une gestion des exceptions pour mettre en évidence le motif.

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
2018-06-29 16:52



.NET Framework 4 introduit une nouvelle méthode "CopyTo" de l'espace de noms Stream Class de System.IO. En utilisant cette méthode, nous pouvons copier un flux dans un autre flux de classe de flux différente.

Voici un exemple pour cela.

    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/>");

0
2018-04-30 12:47