web-dev-qa-db-fra.com

Comment enregistrer un flux dans un fichier en C #?

J'ai un objet StreamReader que j'ai initialisé avec un flux, je souhaite maintenant enregistrer ce flux sur un disque (le flux peut être un .gif ou .jpg ou .pdf).

Code existant:

StreamReader sr = new StreamReader(myOtherObject.InputStream);
  1. J'ai besoin de sauvegarder ceci sur le disque (j'ai le nom du fichier).
  2. À l'avenir, je souhaiterai peut-être stocker ceci dans SQL Server.

J'ai aussi le type d'encodage, dont j'aurai besoin si je le stocke sur SQL Server, correct?

663
Loadman

Comme l'a souligné Tilendor dans la réponse de Jon Skeet, les flux ont une méthode CopyTo depuis .NET 4.

var fileStream = File.Create("C:\\Path\\To\\File");
myOtherObject.InputStream.Seek(0, SeekOrigin.Begin);
myOtherObject.InputStream.CopyTo(fileStream);
fileStream.Close();

Ou avec la syntaxe using:

using (var fileStream = File.Create("C:\\Path\\To\\File"))
{
    myOtherObject.InputStream.Seek(0, SeekOrigin.Begin);
    myOtherObject.InputStream.CopyTo(fileStream);
}
858
Antoine Leclair

Vous ne devez pas utiliser StreamReader pour les fichiers binaires (comme les gifs ou les jpgs). StreamReader est pour les données text . Vous allez presque certainement perdre des données si vous les utilisez pour des données binaires arbitraires. (Si vous utilisez Encoding.GetEncoding (28591), tout ira bien, mais quel est le but?)

Pourquoi avez-vous besoin d'utiliser une StreamReader? Pourquoi ne pas simplement conserver les données binaires sous forme de données binaires et les réécrire sur le disque (ou SQL) sous forme de données binaires?

EDIT: Comme cela semble être quelque chose que les gens veulent voir ... si vous faites vous voulez simplement copier un flux dans un autre (par exemple dans un fichier) utilisez quelque chose comme ceci:

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

Pour l'utiliser pour transférer un flux dans un fichier, par exemple:

_using (Stream file = File.Create(filename))
{
    CopyStream(input, file);
}
_

Notez que Stream.CopyTo a été introduit dans .NET 4, servant essentiellement le même objectif.

502
Jon Skeet
public void CopyStream(Stream stream, string destPath)
{
  using (var fileStream = new FileStream(destPath, FileMode.Create, FileAccess.Write))
  {
    stream.CopyTo(fileStream);
  }
}
70
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

Je n'ai pas toutes les réponses en utilisant CopyTo, où les systèmes utilisant l'application n'ont peut-être pas été mis à niveau vers .NET 4.0+. Je sais que certains voudraient forcer les gens à mettre à jour, mais la compatibilité, c’est aussi bien à Nice.

Autre chose, je n'utilise pas de flux pour copier à partir d'un autre flux. Pourquoi ne pas simplement faire:

byte[] bytes = myOtherObject.InputStream.ToArray();

Une fois que vous avez les octets, vous pouvez facilement les écrire dans un fichier:

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

Ce code fonctionne car je l'ai testé avec un fichier .jpg, bien que je l'avoue, je ne l'ai utilisé qu'avec de petits fichiers (moins de 1 Mo). Un flux, pas de copie entre les flux, pas de codage nécessaire, écrivez simplement les octets! Pas besoin de trop compliquer les choses avec StreamReader si vous avez déjà un flux, vous pouvez convertir en bytes directement avec .ToArray()!

Voici ce que je peux voir comme inconvénient potentiel si vous avez un fichier volumineux que vous avez, l'avoir comme flux et d'utiliser .CopyTo() ou son équivalent, permettant à FileStream de l'écouter au lieu d'utiliser un tableau d'octets et lire les octets un à un. En conséquence, cela pourrait être plus lent. Mais cela ne devrait pas s’étouffer puisque la méthode .Write() de FileStream gère l’écriture des octets et ne le fait qu’un octet à la fois pour ne pas encombrer la mémoire, sauf que vous devrez disposer de suffisamment de mémoire pour contenir le flux sous la forme d'un objet byte[]. Dans ma situation où j’utilisais ceci, obtenant un OracleBlob, je devais aller à un byte[], il était assez petit et, de plus, il n’y avait pas de streaming disponible pour moi, de toute façon, alors je viens d’envoyer mes octets à ma fonction, ci-dessus.

Une autre option, en utilisant un flux, serait de l’utiliser avec la fonction CopyStream de Jon Skeet qui figurait dans une autre publication. Elle utilise simplement FileStream pour prendre le flux d’entrée et en créer le fichier directement. Il n'utilise pas File.Create, comme il l'a fait (ce qui semblait initialement poser problème pour moi, mais a ensuite révélé qu'il s'agissait probablement d'un bogue 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

Pourquoi ne pas utiliser un objet 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

Une autre option consiste à obtenir le flux dans un byte[] et à utiliser File.WriteAllBytes. Cela devrait faire:

using (var stream = new MemoryStream())
{
    input.CopyTo(stream);
    File.WriteAllBytes(file, stream.ToArray());
}

L'emballer dans une méthode d'extension lui donne une meilleure dénomination:

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