12
votes

OutofMemoryException lorsque j'ai lu 500 Mo Filtream

J'utilise filtream pour lire gros fichier (> 500 Mo) et je reçois la sortie exceptionnelle.

Toute solution à ce sujet. P>

Mon code est le suivant: P>

 using (var fs3 = new FileStream(filePath2, FileMode.Open, FileAccess.Read))
                {
                    byte[] b2 = ReadFully(fs3, 1024);
                }


 public static byte[] ReadFully(Stream stream, int initialLength)
    {
        // If we've been passed an unhelpful initial length, just
        // use 32K.
        if (initialLength < 1)
        {
            initialLength = 32768;
        }

        byte[] buffer = new byte[initialLength];
        int read = 0;

        int chunk;
        while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0)
        {
            read += chunk;

            // If we've reached the end of our buffer, check to see if there's
            // any more information
            if (read == buffer.Length)
            {
                int nextByte = stream.ReadByte();

                // End of stream? If so, we're done
                if (nextByte == -1)
                {
                    return buffer;
                }

                // Nope. Resize the buffer, put in the byte we've just
                // read, and continue
                byte[] newBuffer = new byte[buffer.Length * 2];
                Array.Copy(buffer, newBuffer, buffer.Length);
                newBuffer[read] = (byte)nextByte;
                buffer = newBuffer;
                read++;
            }
        }
        // Buffer is now too big. Shrink it.
        byte[] ret = new byte[read];
        Array.Copy(buffer, ret, read);
        return ret;
    }


0 commentaires

3 Réponses :


4
votes

Vous doublez votre taille de tampon à chaque réallocation, ce qui signifie que les blocs précédemment attribués ne peuvent jamais être utilisés (ils fuient efficacement). Au moment où vous arrivez à 500 Mo, vous avez mâché 1 GB plus des frais généraux. En fait, il pourrait être de 2 Go, car, si vous atteignez 512 Mo, votre prochaine allocation sera de 1 Go. Sur un système 32 bits, cela en cache votre processus.

Comme il s'agit d'un fichier normal que vous lisez, faites simplement interroger le système de fichiers pour sa taille et prévenez le tampon en une fois.


8 commentaires

S'il vous plaît, quel est le meilleur code, j'utilise ceci: yoda.arachsys.com/cshaarp/ readbinaire.html merci monsieur


+1: Oui, alloue la taille de la mémoire tampon dont vous avez besoin est une bonne idée ... En fait, je suis surpris que .NET n'a pas de méthode pour lire un fichier entier dans une matrice d'octets ou une autre structure similaire.


Cela fait. Fichier.readallbytes msdn.microsoft.com/en-us / Bibliothèque / ... Mais ce n'est pas ce que cette affiche devrait faire. Lire tous les octets d'un fichier de 500 Mo en mémoire est généralement une mauvaise idée , et dans ce cas, c'est une très mauvaise idée. L'affiche contient clairement un objectif primaire, mais non constitué qui n'est pas "lu tous les octets d'un fichier en mémoire". Il pense Il a besoin de lire tous les octets, mais ce n'est pas vrai.


Juste pour ajouter, j'ai le même problème avec mon Excel Adidin VSTo. Mon code C # tente de readallytes à charger dans les données brutes dans un fichier Excel de 60 Mo, et même qui lance une exception exceptionnelle.


@MIKEDHILL, vous ne devriez généralement pas avoir de difficulté à lire 60 Mo en mémoire. Êtes-vous sur un système contraint de la mémoire avec une mémoire virtuelle éteinte?


@Marcelo: Nope - Ceci est sur un ordinateur portable exécutant Windows 7, Excel 2013 et 16 Go de mémoire.


@MikeGledhill Quelque chose est horriblement tort. Je ne vois pas pourquoi vous obtiendriez une OUM dans ce scénario. Je me demande si peut-être que VSTO impose des restrictions sur les additions peuvent faire. Je suggère que la levée est une nouvelle question si vous ne l'avez pas déjà fait.


@Marcelo: J'ai trouvé une solution de contournement. Je lisais dans tous les octets du fichier Excel, pour l'enregistrer dans un enregistrement de base de données à l'aide de LINQ. La solution de contournement était de faire une copie de fichier, copiant le fichier sur un dossier du serveur de base de données, puis obtenez une procédure stockée sur Bulk-Insérer le fichier dans un enregistrement DB. C'est beaucoup plus rapide et il n'est pas nécessaire de lire dans tous les octets de la machine de l'utilisateur. Mais c'est étrange que je devais faire cela.



39
votes

Le code que vous affichez, lit tout le contenu du fichier 500 Mo dans une région contiguë en mémoire. Il n'est pas surprenant que vous obteniez une condition hors mémoire.

La solution est: "Ne faites pas ça." P>

Que êtes-vous vraiment em> essayer de faire? P>


Si vous souhaitez lire complètement un fichier, c'est beaucoup plus simple que la méthode de la lecture que vous utilisez. Essayez ceci: p> xxx pré>

mais ... en utilisant ce code ne résoudra pas votre problème. Cela pourrait fonctionner pour un fichier de 500 Mo. Cela ne fonctionnera pas pour un fichier de 750 Mo ou un fichier de 1 Go. À un moment donné, vous atteindrez la limite de mémoire de votre système et vous aurez la même erreur de mémoire de mémoire que vous avez démarrée. P>

Le problème est que vous essayez de conserver tout le contenu du fichier en mémoire à la fois. Ceci est généralement inutile et est condamné à l'échec à mesure que les fichiers augmentent de taille. Ce n'est pas un problème lorsque le fichierize est de 16k. À 500 Mo, c'est la mauvaise approche. p>

C'est pourquoi j'ai posé plusieurs fois, qu'est-ce que vous essayez vraiment de faire em>? p>


sonne comme si vous souhaitez envoyer le contenu de un fichier sur un flux de réponses ASPnet. Ceci est la question. Pas "Comment lire un fichier de 500 Mo en mémoire?" Mais "Comment envoyer un fichier volumineux au flux de réponses ASPNET?" P>

Pour cela, encore une fois, c'est assez simple. P>

// emit the contents of a file into the ASPNET Response stream
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) 
{ 
   Response.BufferOutput= false;   // to prevent buffering
   byte[] buffer = new byte[1024];
   int bytesRead = 0;
   while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0) 
   {
       Response.OutputStream.Write(buffer, 0, bytesRead);
   }
} 


9 commentaires

Vous voulez uniquement lire un gros fichier en octet [] à envoyer dans une page ASP.NET. Fonction à grande échelle est le code de yoda.arachsysys.com. Merci !!! YODA.ARECHSYSYSCOM/CSHARP/Readbiny.html


Pourquoi voulez-vous tout le contenu de ce grand fichier en mémoire à la fois? Qu'est-ce que tu es vraiment essayer de faire?


Je veux seulement lire un gros fichier en octet [] pour l'envoyer à ASP.NET page comme une réponse. Fonction à grande échelle est le code de yoda.arachsysys.com. Merci !!! yoda.arachsys.com/cshaarp/readbiny.html


Vous voulez lire 500 Mo dans la mémoire et omettre. Cela signifie que vous n'avez pas assez de mémoire. En outre, FileStream prend en charge une propriété de longueur. Vous n'avez pas besoin de passer à travers toutes les pompes de lecture () pour lire un fichier entier. Il suffit d'attribuer un tampon de longueur de taille et lus une fois. Une approche telle que prises avec prudence () n'est nécessaire que lorsque vous ne pouvez pas connaître la longueur du flux avant de le lire.


Et comment puis-je envoyer le gros fichier gras à la page de réponse ASP.NET ?? Comment puis-je lire un gros fichier en c # ?? Merci, s'il vous plaît, tout exemple de code ??


Merci monsieur, je vais l'essayer. Je veux envoyer le contenu d'un fichier dans un flux de réponses ASPNET. Ma question, "Comment envoyer un fichier volumineux au flux de réponses ASPNET?" Oui. Maintenant, si j'ai Steerver ASP.NET et que les services WCF. Une page ASP.NET appelle ServiceWCF pour obtenir des octets [] du fichier BIG BIF. Il y a une autre question ... :-)


Je conviens que ce n'est pas ce que il veut faire. Mais c'est ce que i veux faire. : \ Ça fait 3 ans et je préférerais ne pas sortir d'erreur sur un fichier de 500 Mo, même s'il est exceptionnellement grand, mais je ne veux pas non plus changer mon approche lorsque la grande majorité des fichiers (vraiment tous) devrait être inférieur à 20 Mo. Je trouverai une solution, tout simplement pas dans ce fil. Je soupçonne que c'est peut-être parce que je suis parti sur X86 en ce moment, mais je ne devrais toujours pas être près de la limite de 2 Go pour les applications.


@Serinus - Vous ne voulez vraiment pas faire ça. Vous n'avez pas besoin de "erreur" sur un fichier de 500 Mo. Il suffit de le diffuser, comme je l'ai dit. La solution que vous recherchez est juste ici. Vous semblez ne pas aimer la solution pour une raison quelconque, mais c'est la solution, je vous assure.


Solution très utile pour la question que j'avais, tout en faisant exactement la même erreur; essayer d'envoyer un gros fichier à la fois. Maintenant, les clients peuvent même voir la progression de téléchargement lorsque le fichier est enfoncé! +1



0
votes

ASP.NET Core Core Middleware Strong>

public static async Task<string> GetRequestBody(HttpContext context)
    {
        string bodyText = string.Empty;
        try
        {
            var requestbody = context.Request.Body;
            context.Request.EnableRewind();
            int offset = 0, bytesread = 0;
            var buffer = new byte[5096];
            while ((bytesread = await context.Request.Body.ReadAsync(buffer, offset, buffer.Length - offset)) > 0)
            {
                offset += bytesread;
                if (offset == buffer.Length)
                {
                    int nextByte = context.Request.Body.ReadByte();
                    if (nextByte == -1)
                    {
                        break;
                    }
                    byte[] newBuffer = new byte[buffer.Length * 2];
                    Array.Copy(buffer, newBuffer, buffer.Length);//how to avoid copy 
                    newBuffer[offset] = (byte)nextByte;//how to avoid boxing 
                    buffer = newBuffer;
                    offset++;
                }
                if (offset > 4194304)
                {
                    //log.Warn("Middleware/GetRequestBody--> Request length exceeding limit");
                    break;
                }
            }
            bodyText = Encoding.UTF8.GetString(buffer);
        }
        catch (Exception ex)
        {
            //log.Debug(ex, "Middleware/GetRequestBody--> Request length exceeding limit");
        }
        context.Request.Body.Position = 0;
        return bodyText;
    }


0 commentaires