870 votes

Comment enregistrer un flux dans un fichier en C# ?

J'ai un StreamReader que j'ai initialisé avec un flux, je veux maintenant sauvegarder ce flux sur le disque (le flux peut être un fichier de type .gif o .jpg o .pdf ).

Code existant :

StreamReader sr = new StreamReader(myOtherObject.InputStream);
  1. Je dois l'enregistrer sur le disque (j'ai le nom du fichier).
  2. À l'avenir, je pourrais vouloir stocker ces données sur le serveur SQL.

J'ai également le type d'encodage, dont j'aurai besoin si je le stocke sur le serveur SQL, correct ?

5 votes

Qu'est-ce que myOtherObject ?

1110voto

Antoine Leclair Points 5818

Comme l'a souligné Tilendor dans la réponse de Jon Skeet, les flux ont une 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 le using la syntaxe :

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

75 votes

Notez que vous devez appeler myOtherObject.InputStream.Seek(0, SeekOrigin.Begin) si vous n'êtes pas déjà au début, sinon vous ne copierez pas le flux entier.

5 votes

Si ce flux d'entrée provient d'une connexion http, est-ce qu'il va mettre en mémoire tampon et télécharger puis écrire tous les octets de la source ?????.

2 votes

J'ai créé une visionneuse PDF où j'utilise un flux, une fois que j'ai lié le flux et que j'enregistre le fichier PDF en utilisant le même flux, sans utiliser "Seek(0, SeekOrigin.Begin)", je ne serai pas en mesure d'enregistrer le document correct. donc +1 pour avoir mentionné ce "Seek(0, SeekOrigin.Begin)".

590voto

Jon Skeet Points 692016

Vous ne doit pas utiliser StreamReader pour les fichiers binaires (comme les gifs ou les jpgs). StreamReader est pour texte données. Vous allez presque certainement perdre des données si vous l'utilisez pour des données binaires arbitraires. (Si vous utilisez Encoding.GetEncoding(28591), vous vous en sortirez probablement, mais à quoi bon).

Pourquoi devez-vous utiliser un StreamReader du tout ? Pourquoi ne pas simplement garder les données binaires comme des données binaires et les réécrire sur le disque (ou SQL) comme des données binaires ?

Comme cela semble être quelque chose que les gens veulent voir... si vous hacer Si vous voulez simplement copier un flux vers un autre (par exemple vers 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 afin de vider un flux vers un fichier, par exemple :

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

Notez que Stream.CopyTo a été introduite dans .NET 4 et sert essentiellement le même objectif.

8 votes

C'est un cas tellement courant que je suis surpris qu'il ne soit pas présent dans .NET. Je vois des gens qui créent des tableaux d'octets de la taille du fichier entier, ce qui peut poser des problèmes pour les gros fichiers.

84 votes

@Tilendor : C'est une méthode d'extension présente dans .NET 4 (CopyTo).

35 votes

Je ne pense pas que ce soit une méthode d'extension, mais c'est nouveau dans la classe Stream.

103voto

Darren Corbett Points 149
public void CopyStream(Stream stream, string destPath)
{
  using (var fileStream = new FileStream(destPath, FileMode.Create, FileAccess.Write))
  {
    stream.CopyTo(fileStream);
  }
}

33 votes

Vous ne devriez probablement pas mettre le stream dans le using(){} entre parenthèses. Votre méthode n'a pas créé le flux, donc elle ne devrait pas s'en débarrasser.

2 votes

Au lieu de cela, vous devez mettre FileStream au lieu de l'utiliser, sinon il sera maintenu ouvert jusqu'à ce qu'il soit ramassé.

3 votes

Cela a bien fonctionné mais j'ai obtenu une sortie de 0 KB. Au lieu de cela, j'ai dû faire ceci pour obtenir un résultat correct : File.WriteAllBytes(destinationFilePath, input.ToArray()); . Dans mon cas, input est un MemoryStream provenant de l'intérieur d'un ZipArchive .

34voto

jhonjairoroa87 Points 29
private void SaveFileStream(String path, Stream stream)
{
    var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
    stream.CopyTo(fileStream);
    fileStream.Dispose();
}

1 votes

Cela a bien fonctionné mais j'ai obtenu une sortie de 0 KB. J'ai dû faire ceci pour obtenir un résultat correct : File.WriteAllBytes(destinationFilePath, input.ToArray()); . Dans mon cas, input est un MemoryStream provenant de l'intérieur d'un ZipArchive .

4 votes

Cela m'a aidé à comprendre ce que je faisais mal. Cependant, n'oubliez pas de vous déplacer au début du flux : stream.Seek(0, SeekOrigin.Begin);

0 votes

stream.Position = 0; est une syntaxe alternative pour se déplacer au début du flux.

7voto

Adrian Points 155

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

48 votes

Et si le flux d'entrée fait 1 Go de long, ce code essaiera d'allouer un tampon de 1 Go :)

1 votes

Cela ne fonctionne pas avec le ResponseStream, car il est de longueur inconnue.

0 votes

Même s'il est vrai que vous devez avoir la mémoire disponible pour l'opération. byte[] Je pense qu'il est rare que vous transmettiez un blob de plus de 1 Go à un fichier... à moins que vous n'ayez un site qui conserve les torrents de DVD... De plus, la plupart des ordinateurs ont au moins 2 Go de RAM disponibles de nos jours, de toute façon....Caveat est valable, mais je pense que c'est un cas où c'est probablement "assez bien" pour la plupart des emplois.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X