Avatar billede baddaydaddy Nybegynder
12. oktober 2010 - 11:35 Der er 9 kommentarer og
2 løsninger

Image.FromFile / Image.FromStream giver OutOfMemoryException

Jeg har fundet en masse om dette problem ved at google lige præcis min overskrift, men de aller fleste tilfælde er i relation til at man forsøger at loade meget store filer eller korrupte/tomme filer. Det er dog IKKE tilfældet for mig, derfor forsøger jeg lige at høre om der er nogen der kan give en løsning her.

I min kode indlæser jeg alle .jpg filer i en valgt mappe. Disse bliver gemt i et objekt og derefter i en generic list (samt en listbox). På et eller andet tidspunkt kan den ikke mere og smider en OutOfMemoryException.

Den essentielle del af koden ser således ud:

if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
    string foldername = folderBrowserDialog1.SelectedPath;
    string[] folderFiles = Directory.GetFiles(foldername, "*.jpg");

    listboxBilder.Items.Clear();
    galleryList = new List<GalleryImage>();
    Image img = null;
    FileStream fs = null;

    foreach (string f in folderFiles)
    {
        try
        {
            fs = new FileStream(f, FileMode.Open, FileAccess.Read);
            img = Image.FromStream(fs, true, true);
        }
        catch (OutOfMemoryException ex)
        {
            // Excepitonhandling
        }

            GalleryImage newObj = new GalleryImage(f, img, info.CreationTime);
            galleryList.Add(newObj);
            listboxBilder.Items.Add(newObj);
        }

        fs.Dispose();
    }
}

Jeg har forsøgt med forskellige mængder billeder og jeg kan i nogle tilfælde indlæse 409 billeder med en samlet størrelse på 64,5 mb (det 410.ende smider exception). Men jeg kan også indlæse 107 billeder (á 184 mb) eller 182 (á 117 mb) inden OutOfMemoryException bliver kastet.

Jeg når jo åbenbart en eller anden form for peak, men er der nogen måde jeg kan undgå dette og stadig bevare min funktionalitet??
Avatar billede lasserasch Juniormester
12. oktober 2010 - 12:18 #1
1. Jeg ville bruge 'using' alle steder hvor det er muligt. Så er du sikker på at du får lukket instanserne når de ikke længere skal bruges.

2. Du får ikke lukket dit image objekt i dit loop.
Det er derfor du får out of memory exceptions.


Din kode skal se sådan ud:

if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                string foldername = folderBrowserDialog1.SelectedPath;
                string[] folderFiles = Directory.GetFiles(foldername, "*.jpg");

                listboxBilder.Items.Clear();
                galleryList = new List<GalleryImage>();
                Image img = null;

                foreach (string f in folderFiles)
                {
                    using (FileStream fs = new FileStream(f, FileMode.Open, FileAccess.Read))
                    {
                        img = Image.FromStream(fs);
                    }
                    GalleryImage newObj = new GalleryImage(f, img, DateTime.Now);
                    galleryList.Add(newObj);
                    listboxBilder.Items.Add(newObj);
                    img.Dispose();
                }
            }



Mvh.
Lasse
Avatar billede lasserasch Juniormester
12. oktober 2010 - 12:21 #2
Smider også lige et svar med det samme.
Avatar billede baddaydaddy Nybegynder
12. oktober 2010 - 12:50 #3
Jeg har forsøgt at bruge img.dispose(), men så kan den ikke længere læse fra det Image objekt jeg har tilføjet galleryList når jeg senere forsøger at tilgå denne.
Avatar billede aaberg Nybegynder
12. oktober 2010 - 12:52 #4
Jeg tror ikke det er en god ide at loade så mange billeder ind i memory på en gang. Du skal huske på, at de fleste billeder fylder meget mere i memory, end de gør på harddisken, da de er komprimeret på harddisken.

I memory fylder alle billeder ca det samme som et bitmap billede på disken.

Havde det ikke været en ide, bare at gemme på referencen til billedet i memory, og loade det ind i memory bare når det skal bruges?
Avatar billede baddaydaddy Nybegynder
12. oktober 2010 - 12:56 #5
Det kan selvfølgelig godt være... Jeg bruger listbox'en til at fremvise det enkelte billede i en picturebox, men jeg kan selvfølgelig bare loade billedet på nyt hver gang det skal vises i stedet for at gemme det i memory.
Avatar billede baddaydaddy Nybegynder
12. oktober 2010 - 13:03 #6
...men så får jeg jo bare samme problem hvis man viser mange billeder efter hinanden. For jeg kan jo ikke dispose billedet jeg lige har assignet til picturebox'en.
Avatar billede lasserasch Juniormester
12. oktober 2010 - 13:28 #7
Det er selvfølgelig rigtigt at du ikke kan bruge objektet længere når du har disposet det.

Men så kunne du gøre noget helt andet.

Du kunne erstatte dit Image i galleryimage med en stream. Det fylder ikke så meget, og så kunne du oprette dit billede ud fra stream objektet.


F.eks. sådan her:
public partial class Form1 : Form
    {
        List<GalleryImage> galleryList = new List<GalleryImage>();
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                string foldername = folderBrowserDialog1.SelectedPath;
                string[] folderFiles = Directory.GetFiles(foldername, "*.jpg");

                listboxBilder.Items.Clear();
                galleryList = new List<GalleryImage>();

                foreach (string f in folderFiles)
                {
                    FileStream fs = new FileStream(f, FileMode.Open, FileAccess.Read);
                    GalleryImage newObj = new GalleryImage(f, fs, DateTime.Now);
                    galleryList.Add(newObj);
                    listboxBilder.Items.Add(newObj);
                }
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            GalleryImage gm = (GalleryImage)listboxBilder.SelectedItem;
            Image img = Image.FromStream(gm.stream);
        }

    }

    public class GalleryImage
    {
        public string file { get; set; }
        public FileStream stream { get; set; }
        public DateTime Created { get; set; }

        public GalleryImage()
        {
        }
        public GalleryImage(string f, FileStream fs, DateTime created)
        {
            file = f;
            Created = created;
            stream = fs;
        }
    }
Avatar billede lasserasch Juniormester
12. oktober 2010 - 13:30 #8
Sorry, aaber_cc's løsning med en reference er den samme.

Var vidst for sent ud :-(


Mvh.
Lasse
Avatar billede aaberg Nybegynder
12. oktober 2010 - 13:42 #9
Er det fuldt størrelses billeder du viser i din listbox?

Hvis det er små billeder, kan du gemme en thumnail i memory, og ha en reference til det originale billede, i tilfælde det fulde billede skal bruges senere.
Avatar billede baddaydaddy Nybegynder
12. oktober 2010 - 22:25 #10
Jeg takke for jeres svar...

Jeg endte med at gemme kun filepath i galleryList og indlæse det på ny fra (GalleryImage)listboxBilder.SelectedItem. Men når jeg så disband'er det forrige billedet inden jeg indlæser et nyt giver det ingen overhead.

Jeg vurderer I begge to gav mig den pegepind jeg behøvede, så smid lige et svar aaberg_cc... Så får I halvdelen hver.
Avatar billede aaberg Nybegynder
12. oktober 2010 - 23:12 #11
svar
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester