Jak skopiować zawartość jednego strumienia do drugiego?

Jaki jest najlepszy sposób na skopiowanie zawartości jednego strumienia do drugiego? Czy istnieje standardowa metoda użytkowa?

Author: David Heffernan, 2008-10-23

12 answers

Od. NET 4.5 jest Stream.CopyToAsync metoda

input.CopyToAsync(output);

To zwróci Task to może być kontynuowane po zakończeniu, jak Tak:

await input.CopyToAsync(output)

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

Zauważ, że w zależności od miejsca wywołania do CopyToAsync, kod, który następuje, może lub nie musi być kontynuowany w tym samym wątku, który go wywołał.

The SynchronizationContext to zostało uchwycone podczas wywoływania await określi, w jakim wątku zostanie wykonana kontynuacja on

Dodatkowo, to wywołanie (i jest to szczegół implementacji, który może ulec zmianie) nadal odczytuje i zapisuje sekwencje (po prostu nie marnuje wątków blokujących zakończenie We / Wy).

Od. NET 4.0, jest Stream.CopyTo metoda

input.CopyTo(output);

Dla. NET 3.5 i przed

W frameworku nie ma nic, co pomogłoby w tym; musisz skopiować zawartość ręcznie, w ten sposób:

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

Uwaga 1: To metoda pozwoli na raportowanie postępów (x bajtów odczytanych do tej pory ...)
Uwaga 2: Dlaczego używać stałego rozmiaru bufora, a nie input.Length? Ponieważ ta długość może nie być dostępna! Z docs :

Jeśli Klasa pochodząca ze Stream nie obsługuje wyszukiwania, wywołania Length, SetLength, Position I Seek wywołują 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/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-24 12:14:53

MemoryStream ma .WriteTo (outstream);

I .NET 4.0 ma.CopyTo na normalnym obiekcie stream.

. 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/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-19 17:08:01

Używam następujących metod rozszerzenia. Zoptymalizowano przeciążenia, gdy jeden strumień jest strumieniem pamięci.

    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/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-10 03:54:43

Podstawowe pytania różnicujące implementacje "CopyStream" to:

  • rozmiar bufora odczytu
  • rozmiar zapisu
  • Czy możemy użyć więcej niż jednego wątku(pisząc podczas czytania).

Odpowiedzi na te pytania skutkują znacznie różnymi implementacjami CopyStream i zależą od tego, jakie strumienie posiadasz i co próbujesz zoptymalizować. "Najlepsza" implementacja musiałaby nawet wiedzieć jaki konkretny sprzęt strumienie czytały i pisały.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-23 15:29:53

W rzeczywistości istnieje mniej intensywny sposób wykonywania kopii strumieniowej. Należy jednak pamiętać, że oznacza to, że można zapisać cały plik w pamięci. Nie próbuj tego używać, jeśli pracujesz z plikami, które mają setki megabajtów lub więcej, bez ostrożności.

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

Uwaga: mogą wystąpić również pewne problemy dotyczące danych binarnych i kodowania znaków.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-06 06:52:04

. NET Framework 4 wprowadza nową metodę" CopyTo " klasy Stream System.IO przestrzeń nazw. Za pomocą tej metody możemy skopiować jeden strumień do innego strumienia o innej klasie strumienia.

Oto przykład na to.

    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/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-27 11:44:17

Niestety, nie ma naprawdę prostego rozwiązania. Możesz spróbować czegoś takiego:

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

Ale problem z tym, że inna Implementacja klasy Stream może zachowywać się inaczej, jeśli nie ma nic do czytania. Strumień odczytujący Plik z lokalnego dysku twardego prawdopodobnie zablokuje się, dopóki operacja odczytu nie odczyta wystarczającej ilości danych z dysku, aby wypełnić bufor i zwróci mniej danych tylko wtedy, gdy dotrze do końca pliku. Z drugiej strony odczyt strumienia z sieci może zwrócić mniej danych, nawet jeśli pozostało więcej danych do odebrania.

Przed użyciem rozwiązania ogólnego zawsze sprawdź dokumentację konkretnej klasy stream, której używasz.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-23 15:26:28

Może być sposób, aby zrobić to bardziej efektywnie, w zależności od rodzaju strumienia, z którym pracujesz. Jeśli możesz przekonwertować jeden lub oba strumienie na MemoryStream, możesz użyć metody GetBuffer do pracy bezpośrednio z tablicą bajtów reprezentującą Twoje dane. Pozwala to na użycie metod takich jak Array.CopyTo, które usuwa wszystkie kwestie poruszone przez fryguyboba. Możesz po prostu zaufać. NET, aby znać optymalny sposób kopiowania danych.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-23 17:24:29

Jeśli chcesz, aby procdure skopiował strumień do innego tego, który umieścił nick jest w porządku, ale brakuje mu resetu pozycji, powinno być

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
}

Ale jeśli jest w trybie runtime nie używając procedury, należy użyć strumienia pamięci

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/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-22 16:44:40

Ponieważ żadna z odpowiedzi nie obejmowała asynchronicznego sposobu kopiowania z jednego strumienia do drugiego, oto wzór, który z powodzeniem wykorzystałem w aplikacji do przesyłania portów do kopiowania danych z jednego strumienia sieciowego do drugiego. Brak obsługi wyjątków, aby podkreślić wzór.

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/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-07 21:05:55

Dla. NET 3.5 i przed próbą:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-29 16:52:36

Łatwe i bezpieczne-tworzenie nowego strumienia z oryginalnego źródła:

    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/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-30 12:47:48