it-roy-ru.com

Как сохранить поток в файл в C #?

У меня есть объект StreamReader, который я инициализировал потоком, теперь я хочу сохранить этот поток на диск (поток может быть .gif или .jpg или .pdf).

Существующий код:

StreamReader sr = new StreamReader(myOtherObject.InputStream);
  1. Мне нужно сохранить это на диск (у меня есть имя файла).
  2. В будущем я могу захотеть сохранить это на SQL Server.

У меня также есть тип кодировки, который мне понадобится, если я сохраню его на SQL Server, правильно?

651
Loadman

Как подчеркнул 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);
}
843
Antoine Leclair

Вы не должны использовать StreamReader для двоичных файлов (таких как gifs или jpgs). StreamReader для текстовых данных. Вы почти наверняка потеряете данные, если будете использовать их для произвольных двоичных данных. (Если вы используете Encoding.GetEncoding (28591), вы, вероятно, будете в порядке, но какой в ​​этом смысл?)

Зачем вам вообще нужно использовать StreamReader? Почему бы просто не сохранить двоичные данные как двоичные данные и записать их обратно на диск (или SQL) в виде двоичных данных?

Правка: Поскольку это, кажется, то, что люди хотят видеть ... если вы делаете просто хотите скопировать один поток в другой (например, в файл) используйте что-то вроде этого:

/// <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, служа в основном той же цели.

495
Jon Skeet
public void CopyStream(Stream stream, string destPath)
{
  using (var fileStream = new FileStream(destPath, FileMode.Create, FileAccess.Write))
  {
    stream.CopyTo(fileStream);
  }
}
66
Darren Corbett
private void SaveFileStream(String path, Stream stream)
{
    var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
    stream.CopyTo(fileStream);
    fileStream.Dispose();
}
22
jhonjairoroa87

Я не получаю все ответы, используя 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[]. В моей ситуации, когда я использовал это, получив OracleBlob, мне пришлось перейти на 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();
}
10
vapcguy
//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.
*/
8
George

Почему бы не использовать объект 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);
     }
}
5
Adrian

Другой вариант - получить поток для 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.
}
4
nawfal
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);
        }
    }
}
4
Angelo