У меня есть StreamReader
объект, который я инициализировал с потоком, теперь я хочу сохранить этот поток на диск (поток может быть .gif
или .jpg
или или .pdf
).
Существующий код:
StreamReader sr = new StreamReader(myOtherObject.InputStream);
- Мне нужно сохранить это на диск (у меня есть имя файла).
- В будущем я могу захотеть сохранить это на SQL Server.
У меня также есть тип кодировки, который мне понадобится, если я сохраню его на SQL Server, правильно?
Вы не должны использовать StreamReader
для двоичных файлов (например, GIF-файлов или JPEG). StreamReader
для текстовых данных. Вы почти наверняка потеряете данные, если будете использовать их для произвольных двоичных данных. (Если вы используете Encoding.GetEncoding (28591), вы, вероятно, будете в порядке, но какой в этом смысл?)
Зачем вам вообще нужно использовать StreamReader
? Почему бы просто не сохранить двоичные данные в виде двоичных данных и записать их обратно на диск (или в SQL) в виде двоичных данных?
EDIT: Как это , кажется, что - то люди хотят видеть ... если вы действительно просто хотите скопировать один поток в другой (например , в файл) используйте что - то вроде этого:
/// <summary>
/// Copies the contents of input to output. Doesn't close either stream.
/// </summary>
public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[8 * 1024];
int len;
while ( (len = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, len);
}
}
Чтобы использовать его для выгрузки потока в файл, например:
using (Stream file = File.Create(filename))
{
CopyStream(input, file);
}
Обратите внимание, что Stream.CopyTo
был введен в .NET 4, служил в основном той же цели.
Почему бы не использовать объект FileStream?
public void SaveStreamToFile(string fileFullPath, Stream stream)
{
if (stream.Length == 0) return;
// Create a FileStream object to write a stream to a file
using (FileStream fileStream = System.IO.File.Create(fileFullPath, (int)stream.Length))
{
// Fill the bytes[] array with the stream data
byte[] bytesInStream = new byte[stream.Length];
stream.Read(bytesInStream, 0, (int)bytesInStream.Length);
// Use FileStream object to write to the specified file
fileStream.Write(bytesInStream, 0, bytesInStream.Length);
}
}
byte[]
, я думаю, что было бы редко, если бы вы передавали 1 ГБ + BLOB-объект в файл ... если у вас нет сайта, на котором есть DVD-потоки ... Плюс во всяком случае, на большинстве компьютеров в настоящее время доступно как минимум 2 ГБ ОЗУ. Caveat действителен, но я думаю, что это тот случай, когда он, вероятно, «достаточно хорош» для большинства задач. Как подчеркнул Tilendor в ответе Джона Скита, у потоков есть CopyTo
метод начиная с .NET 4.
var fileStream = File.Create("C:\\Path\\To\\File");
myOtherObject.InputStream.Seek(0, SeekOrigin.Begin);
myOtherObject.InputStream.CopyTo(fileStream);
fileStream.Close();
Или с using
синтаксисом:
using (var fileStream = File.Create("C:\\Path\\To\\File"))
{
myOtherObject.InputStream.Seek(0, SeekOrigin.Begin);
myOtherObject.InputStream.CopyTo(fileStream);
}
myOtherObject.InputStream.Seek(0, SeekOrigin.Begin)
если вы еще не в начале или вы не будете копировать весь поток. //If you don't have .Net 4.0 :)
public void SaveStreamToFile(Stream stream, string filename)
{
using(Stream destination = File.Create(filename))
Write(stream, destination);
}
//Typically I implement this Write method as a Stream extension method.
//The framework handles buffering.
public void Write(Stream from, Stream to)
{
for(int a = from.ReadByte(); a != -1; a = from.ReadByte())
to.WriteByte( (byte) a );
}
/*
Note, StreamReader is an IEnumerable<Char> while Stream is an IEnumbable<byte>.
The distinction is significant such as in multiple byte character encodings
like Unicode used in .Net where Char is one or more bytes (byte[n]). Also, the
resulting translation from IEnumerable<byte> to IEnumerable<Char> can loose bytes
or insert them (for example, "\n" vs. "\r\n") depending on the StreamReader instance
CurrentEncoding.
*/
public void testdownload(stream input)
{
byte[] buffer = new byte[16345];
using (FileStream fs = new FileStream(this.FullLocalFilePath,
FileMode.Create, FileAccess.Write, FileShare.None))
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
fs.Write(buffer, 0, read);
}
}
}
FileStream
- приятно! public void CopyStream(Stream stream, string destPath)
{
using (var fileStream = new FileStream(destPath, FileMode.Create, FileAccess.Write))
{
stream.CopyTo(fileStream);
}
}
stream
объект в using(){}
скобки. Ваш метод не создал поток, поэтому он не должен избавляться от него. FileStream
вместо этого использовать, иначе он останется открытым до тех пор, пока не будет собран мусор. File.WriteAllBytes(destinationFilePath, input.ToArray());
. В моем случае, input
это MemoryStream
приходит изнутри ZipArchive
. private void SaveFileStream(String path, Stream stream)
{
var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
stream.CopyTo(fileStream);
fileStream.Dispose();
}
File.WriteAllBytes(destinationFilePath, input.ToArray());
. В моем случае, input
это MemoryStream
приходит изнутри ZipArchive
. stream.Seek(0, SeekOrigin.Begin);
Другой вариант - получить поток byte[]
и использовать File.WriteAllBytes
. Это должно сделать:
using (var stream = new MemoryStream())
{
input.CopyTo(stream);
File.WriteAllBytes(file, stream.ToArray());
}
Оборачивая его в метод расширения, вы получите лучшее именование:
public void WriteTo(this Stream input, string file)
{
//your fav write method:
using (var stream = File.Create(file))
{
input.CopyTo(stream);
}
//or
using (var stream = new MemoryStream())
{
input.CopyTo(stream);
File.WriteAllBytes(file, stream.ToArray());
}
//whatever that fits.
}
Я не получаю ответы на все вопросы CopyTo
, где, возможно, системы, использующие приложение, возможно, не были обновлены до .NET 4.0+. Я знаю, что некоторые хотели бы заставить людей обновляться, но совместимость тоже хорошая.
Во-вторых, я не использую поток для копирования из другого потока. Почему бы просто не сделать:
byte[] bytes = myOtherObject.InputStream.ToArray();
Получив байты, вы можете легко записать их в файл:
public static void WriteFile(string fileName, byte[] bytes)
{
string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (!path.EndsWith(@"\")) path += @"\";
if (File.Exists(Path.Combine(path, fileName)))
File.Delete(Path.Combine(path, fileName));
using (FileStream fs = new FileStream(Path.Combine(path, fileName), FileMode.CreateNew, FileAccess.Write))
{
fs.Write(bytes, 0, (int)bytes.Length);
//fs.Close();
}
}
Этот код работает, поскольку я тестировал его с .jpg
файлом, хотя я признаю, что использовал его только с небольшими файлами (менее 1 МБ). Один поток, без копирования между потоками, без кодирования, просто запишите байты! Не нужно слишком усложнять ситуацию, StreamReader
если у вас уже есть поток, который вы можете преобразовать bytes
напрямую .ToArray()
!
Единственный потенциальный недостаток, который я вижу в этом, заключается в том, что если у вас есть большой файл, то есть наличие его в виде потока и использование .CopyTo()
или эквивалентное ему позволяет FileStream
выполнять потоковую передачу вместо использования байтового массива и чтения байтов один за другим. Это может быть медленнее делать это таким образом, в результате. Но он не должен задыхаться, так как .Write()
метод FileStream
дескрипторов записывает байты, и он делает это только один байт за раз, поэтому он не будет засорять память, за исключением того, что у вас будет достаточно памяти, чтобы держать поток как byte[]
объект . В моей ситуации, когда я использовал это, получая a OracleBlob
, мне нужно было перейти к a byte[]
, он был достаточно мал, и, кроме того, у меня не было никакого потока, так или иначе, поэтому я просто отправил свои байты в свою функцию выше.
Другой вариант, использующий поток, состоит в том, чтобы использовать его с CopyStream
функцией Джона Скита, которая была в другом посте - она просто использует, FileStream
чтобы взять входной поток и напрямую создать файл из него. Он не использует File.Create
, как он (который изначально казался мне проблематичным, но позже обнаружил, что это скорее всего ошибка VS) ...
/// <summary>
/// Copies the contents of input to output. Doesn't close either stream.
/// </summary>
public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[8 * 1024];
int len;
while ( (len = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, len);
}
}
public static void WriteFile(string fileName, Stream inputStream)
{
string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (!path.EndsWith(@"\")) path += @"\";
if (File.Exists(Path.Combine(path, fileName)))
File.Delete(Path.Combine(path, fileName));
using (FileStream fs = new FileStream(Path.Combine(path, fileName), FileMode.CreateNew, FileAccess.Write)
{
CopyStream(inputStream, fs);
}
inputStream.Close();
inputStream.Flush();
}
Close
из-заusing()
inputStream.Close()
, посмотрите еще раз - inputStream
отправляется как переменная. using
Находится на path+filename
выходном потоке. Если вы говорили fs.Close()
в середине using
, извините, вы были правы об этом, и я удалил это. Вот пример, который использует правильное использование и реализацию idisposable:
static void WriteToFile(string sourceFile, string destinationfile, bool append = true, int bufferSize = 4096)
{
using (var sourceFileStream = new FileStream(sourceFile, FileMode.OpenOrCreate))
{
using (var destinationFileStream = new FileStream(destinationfile, FileMode.OpenOrCreate))
{
while (sourceFileStream.Position < sourceFileStream.Length)
{
destinationFileStream.WriteByte((byte)sourceFileStream.ReadByte());
}
}
}
}
... а также есть это
public static void WriteToFile(FileStream stream, string destinationFile, int bufferSize = 4096, FileMode mode = FileMode.OpenOrCreate, FileAccess access = FileAccess.ReadWrite, FileShare share = FileShare.ReadWrite)
{
using (var destinationFileStream = new FileStream(destinationFile, mode, access, share))
{
while (stream.Position < stream.Length)
{
destinationFileStream.WriteByte((byte)stream.ReadByte());
}
}
}
Ключевым моментом является понимание правильного использования использования (которое должно быть реализовано при создании экземпляра объекта, который реализует идентифицируемый, как показано выше), и хорошее представление о том, как свойства работают для потоков. Позиция - это буквально индекс в потоке (который начинается с 0), за которым следует чтение каждого байта с использованием метода readbyte. В этом случае я, по сути, использую его вместо переменной цикла for и просто позволяю ему следовать по всему пути вплоть до длины, которая буквально является концом всего потока (в байтах). Игнорируйте в байтах, потому что это практически то же самое, и у вас будет что-то простое и элегантное, как это, которое решает все чисто.
Имейте также в виду, что метод ReadByte просто преобразует байт в процесс и может быть просто преобразован обратно.
Я добавлю еще одну реализацию, которую я недавно написал, чтобы создать динамический буфер, чтобы обеспечить последовательную запись данных для предотвращения массивной перегрузки.
private void StreamBuffer(Stream stream, int buffer)
{
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
var memoryBuffer = memoryStream.GetBuffer();
for (int i = 0; i < memoryBuffer.Length;)
{
var networkBuffer = new byte[buffer];
for (int j = 0; j < networkBuffer.Length && i < memoryBuffer.Length; j++)
{
networkBuffer[j] = memoryBuffer[i];
i++;
}
//Assuming destination file
destinationFileStream.Write(networkBuffer, 0, networkBuffer.Length);
}
}
}
Объяснение довольно простое: мы знаем, что нам нужно помнить весь набор данных, которые мы хотим записать, а также то, что мы хотим записывать только определенные суммы, поэтому мы хотим, чтобы первый цикл с последним параметром был пустым (так же, как и while ). Затем мы инициализируем буфер байтового массива, размер которого соответствует размеру переданного, и со вторым циклом мы сравниваем j с размером буфера и размером исходного, и если он больше, чем размер исходного байтовый массив, конец цикла.