Być może, co ważniejsze, w tym momencie, w jaki sposób kopiujesz zawartość „strumieniowo”, co oznacza, że kopiuje ona tylko strumień źródłowy, ponieważ coś zużywa strumień docelowy ...?
Zwróci wartość, Taskktórą można kontynuować po zakończeniu, np .:
await input.CopyToAsync(output)// Code from here on will be run in a continuation.
Zauważ, że w zależności od tego, gdzie CopyToAsyncjest wykonywane połączenie, następujący kod może, ale nie musi, kontynuować w tym samym wątku, który go wywołał.
To, SynchronizationContextco zostało przechwycone podczas wywoływania await, określi, w jakim wątku będzie kontynuowana kontynuacja.
Ponadto to wywołanie (i jest to szczegół implementacji podlegający zmianom) nadal sekwencyjnie odczytuje i zapisuje (nie marnuje nic blokowania wątków po zakończeniu operacji we / wy).
Uwaga 1: Ta metoda pozwoli Ci raportować postęp (dotychczas odczytane bajty x ...)
Uwaga 2: Dlaczego warto używać stałego rozmiaru bufora, a nie input.Length? Ponieważ ta długość może być niedostępna! Z dokumentów :
Jeśli klasa wywodząca się ze strumienia nie obsługuje wyszukiwania, wywołania Length, SetLength, Position i Seek generują NotSupportedException.
Pamiętaj, że nie jest to najszybszy sposób na zrobienie tego. W dostarczonym fragmencie kodu musisz poczekać na zakończenie zapisu przed odczytaniem nowego bloku. Podczas asynchronicznego odczytu i zapisu to oczekiwanie zniknie. W niektórych sytuacjach spowoduje to, że kopia będzie dwa razy szybsza. Sprawi to jednak, że kod będzie znacznie bardziej skomplikowany, więc jeśli prędkość nie stanowi problemu, zachowaj prostotę i skorzystaj z tej prostej pętli. To pytanie na StackOverflow zawiera kod ilustrujący asynchroniczny odczyt / zapis: stackoverflow.com/questions/1540658/... Pozdrawiam, Sebastiaan
Sebastiaan M
16
FWIW, w moich testach stwierdziłem, że 4096 jest w rzeczywistości szybszy niż 32 KB. Ma to coś wspólnego z tym, jak CLR przydziela porcje o określonym rozmiarze. Z tego powodu implementacja .NET Stream.CopyTo .NET najwyraźniej używa 4096.
Jeff
1
Jeśli chcesz wiedzieć, jak zaimplementowano CopyToAsync lub dokonać modyfikacji tak jak ja (musiałem mieć możliwość określenia maksymalnej liczby bajtów do skopiowania), jest on dostępny jako CopyStreamToStreamAsync w „Próbkach do programowania równoległego z .NET Framework” code.msdn .microsoft.com / ParExtSamples
Michael
1
FIY, optymalny rozmiar bufora to 81920bajty, nie32768
Nie widzę wielu próbek w Internecie za pomocą tych metod. Czy to dlatego, że są dość nowe, czy są jakieś ograniczenia?
GeneS
3
To dlatego, że są nowe w .NET 4.0. Stream.CopyTo () zasadniczo robi dokładnie to samo dla pętli, co zatwierdzona odpowiedź, z kilkoma dodatkowymi sprawdzeniami poprawności. Domyślny rozmiar bufora to 4096, ale istnieje również przeciążenie, aby określić większy.
Michael Edenfield
9
Strumień należy przewinąć do tyłu po kopiowaniu: instream.Position = 0;
Draykos
6
Oprócz przewijania strumienia wejściowego zauważyłem także potrzebę przewijania strumienia wyjściowego: outstream.Position = 0;
JonH
32
Używam następujących metod rozszerzenia. Mają zoptymalizowane przeciążenia, gdy jeden strumień jest MemoryStream.
Podstawowe pytania odróżniające implementacje „CopyStream” to:
rozmiar bufora odczytu
rozmiar pisze
Czy możemy użyć więcej niż jednego wątku (pisanie podczas czytania)?
Odpowiedzi na te pytania prowadzą do bardzo różnych implementacji CopyStream i zależą od rodzaju posiadanych strumieni i tego, co próbujesz zoptymalizować. „Najlepsza” implementacja musiałaby nawet wiedzieć, na jakim konkretnym sprzęcie czytały i zapisywały strumienie.
... lub najlepsza implementacja może mieć przeciążenia, które pozwalają określić rozmiar bufora, rozmiar zapisu i czy dozwolone są wątki?
MarkJ
1
W rzeczywistości istnieje mniej wymagający sposób wykonywania kopii strumieniowej. Pamiętaj jednak, że oznacza to, że możesz zapisać cały plik w pamięci. Nie próbuj tego używać, jeśli pracujesz z plikami, które przechodzą do setek megabajtów lub więcej, bez zachowania ostrożności.
publicstaticvoidCopyStream(Stream input,Stream output){
using (StreamReader reader =newStreamReader(input))
using (StreamWriter writer =newStreamWriter(output)){
writer.Write(reader.ReadToEnd());}}
UWAGA: Mogą również występować pewne problemy dotyczące danych binarnych i kodowania znaków.
Domyślny konstruktor StreamWriter tworzy strumień UTF8 bez BOM ( msdn.microsoft.com/en-us/library/fysy0a4b.aspx ), więc nie ma niebezpieczeństwa problemów z kodowaniem. Dane binarne prawie na pewno nie powinny być kopiowane w ten sposób.
kͩeͣmͮpͥ ͩ
14
można łatwo argumentować, że ładowanie „całego pliku do pamięci” nie jest uważane za „mniej obciążające”.
Seph
dostaję wyjątek pamięci z tego powodu
ColacX
To nie jest strumień po strumieniu. reader.ReadToEnd()umieszcza wszystko w pamięci RAM
Bizhan
1
.NET Framework 4 wprowadza nową metodę „CopyTo” w klasie strumieni przestrzeni nazw System.IO. Za pomocą tej metody możemy skopiować jeden strumień do innego strumienia innej klasy strumienia.
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, jeśli 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.
Zawsze sprawdzaj dokumentację konkretnej klasy strumienia, której używasz przed użyciem ogólnego rozwiązania.
Ogólne rozwiązanie będzie działać tutaj - odpowiedź Nicka jest dobra. Rozmiar bufora jest oczywiście arbitralnym wyborem, ale 32K brzmi rozsądnie. Myślę, że rozwiązaniem Nicka jest nie zamykać strumieni - pozostaw to właścicielowi.
Jon Skeet
0
Może istnieć sposób, aby to zrobić 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 bezpośredniej pracy z tablicą bajtów reprezentującą twoje dane. Pozwala to na użycie metod takich jak Array.CopyTo, które usuwają wszystkie problemy podniesione przez fryguybob. Możesz po prostu zaufać platformie .NET, która zna optymalny sposób kopiowania danych.
jeśli chcesz, aby procedura skopiowała strumień na inny, który Nick opublikował, jest w porządku, ale brakuje mu resetowania pozycji, powinno być
publicstaticvoidCopyStream(Stream input,Stream output){byte[] buffer =newbyte[32768];longTempPos= 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 środowisku wykonawczym i nie korzysta z procedury, należy użyć strumienia pamięci
Stream output =newMemoryStream();byte[] buffer =newbyte[32768];// or you specify the size you want of your bufferlongTempPos= 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
Nie należy zmieniać pozycji strumienia wejściowego, ponieważ nie wszystkie strumienie umożliwiają losowy dostęp. Na przykład w strumieniu sieciowym nie można zmieniać pozycji, tylko czytać i / lub pisać.
R. Martinho Fernandes
0
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 przekierowującej porty do kopiowania danych z jednego strumienia sieciowego do drugiego. Brakuje obsługi wyjątków, aby podkreślić wzór.
constint BUFFER_SIZE =4096;staticbyte[] bufferForRead =newbyte[BUFFER_SIZE];staticbyte[] bufferForWrite =newbyte[BUFFER_SIZE];staticStream sourceStream =newMemoryStream();staticStream destinationStream =newMemoryStream();staticvoidMain(string[] args){// Initial read from source stream
sourceStream.BeginRead(bufferForRead,0, BUFFER_SIZE,BeginReadCallback,null);}privatestaticvoidBeginReadCallback(IAsyncResult asyncRes){// Finish reading from source streamint bytesRead = sourceStream.EndRead(asyncRes);// Make a copy of the buffer as we'll start another read immediatelyArray.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);}privatestaticvoidBeginWriteCallback(IAsyncResult asyncRes){// Finish writing to destination stream
destinationStream.EndWrite(asyncRes);}
Z pewnością, jeśli drugi odczyt zakończy się przed pierwszym zapisem, wówczas nadpisujesz zawartość buforaForWrite z pierwszego odczytu, zanim zostanie on zapisany.
Odpowiedzi:
Począwszy od .NET 4.5 istnieje
Stream.CopyToAsync
metodaZwróci wartość,
Task
którą można kontynuować po zakończeniu, np .:Zauważ, że w zależności od tego, gdzie
CopyToAsync
jest wykonywane połączenie, następujący kod może, ale nie musi, kontynuować w tym samym wątku, który go wywołał.To,
SynchronizationContext
co zostało przechwycone podczas wywoływaniaawait
, określi, w jakim wątku będzie kontynuowana kontynuacja.Ponadto to wywołanie (i jest to szczegół implementacji podlegający zmianom) nadal sekwencyjnie odczytuje i zapisuje (nie marnuje nic blokowania wątków po zakończeniu operacji we / wy).
Począwszy od .NET 4.0 istnieje
Stream.CopyTo
metodaDla .NET 3.5 i wcześniejszych
Nie ma w tym nic upieczonego, aby pomóc w tym; musisz skopiować zawartość ręcznie, tak jak:
Uwaga 1: Ta metoda pozwoli Ci raportować postęp (dotychczas odczytane bajty x ...)
Uwaga 2: Dlaczego warto używać stałego rozmiaru bufora, a nie
input.Length
? Ponieważ ta długość może być niedostępna! Z dokumentów :źródło
81920
bajty, nie32768
MemoryStream ma .WriteTo (outstream);
a .NET 4.0 ma .CopyTo na normalnym obiekcie strumieniowym.
.NET 4.0:
źródło
Używam następujących metod rozszerzenia. Mają zoptymalizowane przeciążenia, gdy jeden strumień jest MemoryStream.
źródło
Podstawowe pytania odróżniające implementacje „CopyStream” to:
Odpowiedzi na te pytania prowadzą do bardzo różnych implementacji CopyStream i zależą od rodzaju posiadanych strumieni i tego, co próbujesz zoptymalizować. „Najlepsza” implementacja musiałaby nawet wiedzieć, na jakim konkretnym sprzęcie czytały i zapisywały strumienie.
źródło
W rzeczywistości istnieje mniej wymagający sposób wykonywania kopii strumieniowej. Pamiętaj jednak, że oznacza to, że możesz zapisać cały plik w pamięci. Nie próbuj tego używać, jeśli pracujesz z plikami, które przechodzą do setek megabajtów lub więcej, bez zachowania ostrożności.
UWAGA: Mogą również występować pewne problemy dotyczące danych binarnych i kodowania znaków.
źródło
reader.ReadToEnd()
umieszcza wszystko w pamięci RAM.NET Framework 4 wprowadza nową metodę „CopyTo” w klasie strumieni przestrzeni nazw System.IO. Za pomocą tej metody możemy skopiować jeden strumień do innego strumienia innej klasy strumienia.
Oto przykład tego.
źródło
CopyToAsync()
jest zalecane.Niestety nie ma naprawdę prostego rozwiązania. Możesz spróbować czegoś takiego:
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, jeśli 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.
Zawsze sprawdzaj dokumentację konkretnej klasy strumienia, której używasz przed użyciem ogólnego rozwiązania.
źródło
Może istnieć sposób, aby to zrobić 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 bezpośredniej pracy z tablicą bajtów reprezentującą twoje dane. Pozwala to na użycie metod takich jak Array.CopyTo, które usuwają wszystkie problemy podniesione przez fryguybob. Możesz po prostu zaufać platformie .NET, która zna optymalny sposób kopiowania danych.
źródło
jeśli chcesz, aby procedura skopiowała strumień na inny, który Nick opublikował, jest w porządku, ale brakuje mu resetowania pozycji, powinno być
ale jeśli jest w środowisku wykonawczym i nie korzysta z procedury, należy użyć strumienia pamięci
źródło
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 przekierowującej porty do kopiowania danych z jednego strumienia sieciowego do drugiego. Brakuje obsługi wyjątków, aby podkreślić wzór.
źródło
W przypadku .NET 3.5 i przed wypróbowaniem:
źródło
Łatwe i bezpieczne - utwórz nowy strumień z oryginalnego źródła:
źródło