Benutzer mit den meisten Antworten
Image aus Cache laden - sehr langsam

Frage
-
Guten Tag,
ich habe eine Datenbank, in welcher Bilder binär gespeichert sind.
Auf einer ASPX-Seite werden die Bilder in einer Listview ausgegeben, etwa 20 pro Seite.Damit die Bilder nicht jedes mal aus der Datenbank geladen werden müssen, wollte ich die Bilder im Cache speichern
und daraus auslesen.
Für jedes Bild wird die Methode aufgerufen:private bool getImage(HttpContext context) { if (context.Request.QueryString["artId"] != null) { //Es wird eine ID übergeben und die dazugehörige ImageID ausgelesen Int16 artikelID = Convert.ToInt16(context.Request.QueryString["artId"]); string query = "SELECT ImageID FROM artBilderZuordnung WHERE ArtikelID = "+artikelID+";"; SqlDataReader reader = usr.sqlSelect(query); int imageID = 0; while (reader.Read()) { imageID = Convert.ToInt16(reader[0]); } reader.Dispose(); reader.Close(); //Wenn kein Image zur ID da ist, wird das Default Image geladen(ID=4) if (imageID == 0) { imageID = 4; } Context.Response.ClearContent(); //Wenn das Image im Cache ist, soll es daraus gelesen werden if (Cache[Convert.ToString(imageID)] != null) { System.Drawing.Image imgFromGB = (System.Drawing.Image)Cache[Convert.ToString(imageID)]; imgFromGB.Save(Context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg); return true; } //wenn das Image nicht im Cache ist, wird es aus der DB gelesen query = "SELECT Image FROM artBilder WHERE ID = " + imageID + ";"; context.Response.ContentType = "image/jpeg"; reader = usr.sqlSelect(query); object img = new object(); while (reader.Read()) { img = reader[0]; } if (img != null) { try { MemoryStream ms = new MemoryStream((byte[])img, false); System.Drawing.Image imgFromGB = System.Drawing.Image.FromStream(ms); context.Response.ClearContent(); int width = imgFromGB.Width; int height = imgFromGB.Height; System.Drawing.Image.GetThumbnailImageAbort myCallback = new System.Drawing.Image.GetThumbnailImageAbort(temp); System.Drawing.Image myThumbnail = imgFromGB.GetThumbnailImage(width, height, myCallback, IntPtr.Zero); myThumbnail.Save(Context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg); //Das Bild wird in den Cache geladen Cache[Convert.ToString(imageID) + "akThumb"] = myThumbnail; } catch (Exception) { return false; } } reader.Close(); } return true; }
Das ganze ist aber sehr langsam, selbst wenn die Bilder aus dem Cache geladen werden. Jeder Bild braucht ca 0,8 Sekunden bis es geladen ist.
Wie würdet ihr das machen? Ein völlig anderer Prozess oder liegt es an der Programmierung?
Grüße
Antworten
-
Hi,
zum einen sollte man natürlich keine WebForm dazu mißbrauchen, Bilder, ... auszuliefern. Dafür gibt es generisches Handler (ASHX). Die machen dasselbe, haben aber den ganzen Overhead für Laden, Rendern, ... der HTML Inhalte nicht.
Zum anderen könnte es sein, dass Du das Projekt im Debug Modus laufen lässt. Schau mal, ob das Projekt wirklich als Release kompiliert wurde und ob in der web.config ggfs. debug="true" steht. Falls ja, ändere Debug auf "false" und kompilier das Projekt explizit im Release Modus.
Zum laden von Dateien: Du kannst hier auch Response.TransmitFile( "~/Images/Slide.gif" ) nehmen, das ist erheblich schneller als das vorherige Laden in ein Image Objekt.
Wichtig ist auch, dass Du alle Objekte, insbesondere was Grafiken, Connections, ... angeht immer schließt und falls eine Dispose Methode vorhanden ist, diese auch aufrufst. Also bspw. image.Dispose(); ... Ansonsten hast Du zum einen gesperrte Resourcen und zum anderen recht schnell ein Speicherproblem.
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community- Als Antwort markiert StevS Freitag, 20. April 2012 18:51
Alle Antworten
-
Hi,
zum einen liest Du bei jedem Request Daten aus der Datenbank. Das sollte doch eigentlich nicht notwendig sein.
Dann ist der Cache für alle Requests derselbe, wenn mehrere Requests gleichzeitig eingehen, blockiert sich das ggfs. gegenseitig.
Ich persönlich würde die Bilder nicht im Cache ablegen, sondern die Thumbnails erzeugen und auf der Platte ablegen und diese dann direkt verlinken. IIS kann das sehr gut selbst cachen. 0,8 s pro Bild erscheint mir sehr hoch, es sei denn, die Bilder sind selbst als Thumbnail noch sehr groß.
Generell ist GetThumbnailImage die schlechteste Wahl, die Qualität der Thumbnails ist nicht wirklich toll. Schau mal, da findest Du eine Alternative.
http://forums.asp.net/t/1411512.aspx/1
Und zu guter letzt: Ich würde die Bilder nicht in der Datenbank vorhalten. Wenn überhaupt, nur als FILESTREAM, wobei mir auch das hier nicht sinnvoll erscheint. Gibt es zwingende Gründe, warum Du die Bilder in die Datenbank schreibst?
---
Ein wichtiger Hinweis: Du hast mit solchen Konstrukten
string query = "SELECT ImageID FROM artBilderZuordnung WHERE ArtikelID = "+artikelID+";";
ein ziemliches SQL Injection Problem. Arbeite lieber mit Parametern.
Du konvertierst zwar noch den Wert aus dem QueryString in Int16, das würde hier schon auf Fehler laufen, bei Strings hast Du aber definitiv ein Problem.
Zum ausprobieren einfach mal 123'456 bei einem solchen Übergabeparameter in der Adresszeile angeben. Alternativ auch mal ' OR 1 = 1 oder auch ';DELETE FROM ...
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community
- Bearbeitet Stefan FalzModerator Freitag, 20. April 2012 11:16
-
Vielen Dank für Ihre Antwort. Ich werde da wohl noch einiges ändern.
Es ist eine bestehend Datenbank und dort sind die Bilder bereits gespeichert, ich übernehme die gegebene Struktur.
Nachdem ich nochmals einiges getestet hatte, ist mir aufgefallen, dass das Problem woanders liegt (oder zusätzlich woanders).
Selbst wenn ich die Methode ausblende und die Bilder direkt anzeige:System.Drawing.Image image = System.Drawing.Image.FromFile(@"slide.gif"); image.Save(Context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
ist es so langsam.
Deswegen ein Schritt weiter:
Die Bilder werden auf einer anderen Seite in einem Listview angezeigt (gefütter aus einer sqlDataSource).
Dort sieht die Stelle wie folgt aus:<asp:Image ID="imageVorschau" runat="server" width="60px" Visible="true" ImageUrl='/Image.aspx?artId=<%# "+Eval("ID")+" %>' />
Dies wird natürlich für jedes Listitem ausgeführt. Kann es daran liegen, dass die Image.aspx so oft aufgerufen wird?Danke für die Antworten.
-
Hi,
zum einen sollte man natürlich keine WebForm dazu mißbrauchen, Bilder, ... auszuliefern. Dafür gibt es generisches Handler (ASHX). Die machen dasselbe, haben aber den ganzen Overhead für Laden, Rendern, ... der HTML Inhalte nicht.
Zum anderen könnte es sein, dass Du das Projekt im Debug Modus laufen lässt. Schau mal, ob das Projekt wirklich als Release kompiliert wurde und ob in der web.config ggfs. debug="true" steht. Falls ja, ändere Debug auf "false" und kompilier das Projekt explizit im Release Modus.
Zum laden von Dateien: Du kannst hier auch Response.TransmitFile( "~/Images/Slide.gif" ) nehmen, das ist erheblich schneller als das vorherige Laden in ein Image Objekt.
Wichtig ist auch, dass Du alle Objekte, insbesondere was Grafiken, Connections, ... angeht immer schließt und falls eine Dispose Methode vorhanden ist, diese auch aufrufst. Also bspw. image.Dispose(); ... Ansonsten hast Du zum einen gesperrte Resourcen und zum anderen recht schnell ein Speicherproblem.
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community- Als Antwort markiert StevS Freitag, 20. April 2012 18:51