Каков наилучший способ скопировать содержимое одного потока в другой? Есть ли стандартный метод утилиты для этого?Как скопировать содержимое одного потока в другой?
ответ
С .NET 4.5 на, есть Stream.CopyToAsync
method
input.CopyToAsync(output);
Это возвращает Task
, который может быть продолжен, когда завершено, например, так:
await input.CopyToAsync(output)
// Code from here on will be run in a continuation.
Обратите внимание, что в зависимости от где выполняется вызов CopyToAsync
, следующий код может или не может продолжаться в том же потоке, который вызвал его.
SynchronizationContext
, который был снят при вызове await
, определит, в каком потоке будет выполняться продолжение.
Кроме того, этот вызов (и это деталь реализации, подлежащая изменению), все еще последовательности чтения и записи (он просто не теряет блокировки потоков при завершении ввода/вывода).
С .NET 4.0 на, есть это Stream.CopyTo
method
input.CopyTo(output);
для .NET 3.5 и до
Существует нет ничего запеченный в рамки, чтобы помочь с этим; вам необходимо скопировать содержимое вручную, например:
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);
}
}
Обратите внимание, что это не самый быстрый способ сделать это. В предоставленном фрагменте кода вам нужно дождаться завершения записи до того, как будет прочитан новый блок. При выполнении асинхронного чтения и записи это ожидание исчезнет. В некоторой ситуации это сделает копию в два раза быстрее. Однако это сделает код намного сложнее, поэтому, если скорость не является проблемой, сохраните ее просто и используйте этот простой цикл. Этот вопрос в StackOverflow содержит код, который иллюстрирует асинхронный просмотр/запись: http: // stackoverflow.com/questions/1540658/net-asynchronous-stream-read-write С уважением, Sebastiaan – 2009-11-06 07:10:02
Является 32,768 рекомендованным размером байта? Я видел меньшие суммы до, например, 4096. – Josh 2011-01-07 16:02:34
@Josh 32,768 - это копия с кусками размером 32 КБ. 4096 использует куски 4 КБ. Это будет немного медленнее, но используйте меньше памяти. – Nick 2011-01-07 16:11:10
К сожалению, на самом деле нет простого решения. Вы можете попробовать что-то вроде этого:
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();
Но проблема в том, что различные реализации класса потока может вести себя по-другому, если нет ничего, чтобы читать. Поток, считывающий файл с локального жесткого диска, вероятно, будет заблокирован до тех пор, пока операция чтения не будет считывать достаточное количество данных с диска, чтобы заполнить буфер, и только вернуть меньше данных, если оно достигнет конца файла. С другой стороны, чтение потока из сети может возвращать меньше данных, даже если есть больше данных, которые нужно получить.
Всегда проверяйте документацию по определенному классу потока, который вы используете, прежде чем использовать общее решение.
Основные вопросы, которые дифференцируют реализации «CopyStream» является:
- размера буфера чтения
- размера записи
- Можно ли использовать более чем один поток (пишущую в то время как мы читаем).
Ответы на эти вопросы приводят к значительно различным реализациям CopyStream и зависят от того, какие потоки у вас есть и что вы пытаетесь оптимизировать. «Лучшая» реализация даже потребовала бы знать, какое конкретное оборудование, которое потоки читали и записывали.
Возможно, есть способ сделать это более эффективно, в зависимости от того, с каким потоком вы работаете. Если вы можете конвертировать один или оба ваших потока в MemoryStream, вы можете использовать метод GetBuffer для работы напрямую с массивом байтов, представляющим ваши данные. Это позволяет использовать такие методы, как Array.CopyTo, которые абстрагируют все проблемы, поднятые fryguybob. Вы можете просто доверять .NET, чтобы узнать оптимальный способ копирования данных.
если вы хотите procdure скопировать поток в другой тот, который ник размещен в порядке, но отсутствует сброс позиции, она должна быть
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
}
, но если во время выполнения не использует процедуру вы используете поток памяти shpuld
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
Фактически, менее тяжелый способ сделать копию потока. Обратите внимание, что это означает, что вы можете сохранить весь файл в памяти. Не пытайтесь использовать это, если вы работаете с файлами, которые входят в сотни мегабайт или более, без осторожности.
public static void CopyStream(Stream input, Stream output)
{
using (StreamReader reader = new StreamReader(input))
using (StreamWriter writer = new StreamWriter(output))
{
writer.Write(reader.ReadToEnd());
}
}
ПРИМЕЧАНИЕ: Там могут быть некоторые вопросы, касающиеся двоичного кодирования данных и символов.
Я использую следующие методы расширения. Они оптимизировали перегрузки, когда один поток является 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);
}
MemoryStream имеет .WriteTo (outstream);
и .NET 4.0 имеет .CopyTo для обычного объекта потока.
.NET 4.0:
instream.CopyTo(outstream);
Поскольку ни один из ответов не покрыл асинхронный способ копирования из одного потока в другой, вот шаблон, который я успешно использовал в приложении переадресации порта для копирования данные из одного сетевого потока в другой. В нем отсутствует обработка исключений, чтобы подчеркнуть шаблон.
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);
}
.NET Framework 4 представляет новый метод «CopyTo» класса Stream Class пространства System.IO. Используя этот метод, мы можем скопировать один поток в другой поток другого класса потока.
Вот пример этого.
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/>");
Для .NET 3.5 и до попытки:
MemoryStream1.WriteTo(MemoryStream2);
Возможно, что более важно в этот момент, как вы скопировать содержимое «streamably», что означает, что она только копирует поток источника как что-то потребляет поток назначения ...? – drzaus 2015-10-13 22:34:11