# JSP Custom Tag für Bilder



## Marsman (24. Feb 2009)

Hallo Ihr!

Ich habe eine Taglib mit einigen eigenen JSP-Tags. Bisher habe ich allerdings nur Text ausgegeben und möchte mir nun ein Tag zur Anzeige eines Bildes bauen. Ich habe gelesen, dass dazu als Response der ContentType "image/gif" gesetzt werden muss. Das alleine reicht offenbar aber nicht.

Wie kann ich ein Image-Tag programmieren? Leider finde ich keine Beispiele dazu. Eher so Konstruktionen mit einem speziellen Servlet, das vom Standard-IMG Tag aufgerufen wird. Und JSF kann ich hier nicht verwenden.

Danke, Titus


----------



## maki (24. Feb 2009)

IMG Tags zeigen auf eine URL, also wieder nur Text, wieso sollte das nicht gehen? 

>> Und JSF kann ich hier nicht verwenden.

Wieso nicht?


----------



## HLX (24. Feb 2009)

Willst du einfach nur das HTML-Tag wrappen oder soll dein Tag irgendwas besonderes können?


----------



## Marsman (24. Feb 2009)

HLX hat gesagt.:


> Willst du einfach nur das HTML-Tag wrappen oder soll dein Tag irgendwas besonderes können?



Mein Tag soll skalieren und cachen können. Sicherlich könnte ich in der JSP hinter meinem Tag auch ein IMG-Tag mit der URL zum skalierten Bild hinzufügen. Das finde ich aber blöd.

Titus


----------



## HLX (24. Feb 2009)

Das Caching wird doch automatisch vom Browser übernommen, es sei denn es wird über die Browsereinstellungen oder folgenden Eintrag auf der HTML-Seite verhindert

```
<meta http-equiv="cache-control" content="no-cache">
```

Zur Skalierung kannst du das Bild in deinem Tag als BufferedImage einlesen.


----------



## Marsman (24. Feb 2009)

HLX hat gesagt.:


> Das Caching wird doch automatisch vom Browser übernommen, es sei denn es wird über die Browsereinstellungen oder folgenden Eintrag auf der HTML-Seite verhindert... Zur Skalierung kannst du das Bild in deinem Tag als BufferedImage einlesen.



Jo, das weiß ich. User, die das erste Mal auf der Seite sind, haben sie aber nicht im Cache. Und weil das Skalieren mit guter Qualität den Server belastet, möchte ich die Bilder dort cachen.

Aber davon abgesehen: Die Logik ist soweit fertig. In meiner Frage ging es mir darum, wie ich eine Image-Tag realisiere. Bisher arbeitet das ganze etwa so:


```
<tk:image inFile="/image.jpg" outFile="outImage.jpg" width="100" height="100" />
<img src="/cachePath/outImage.jpg" ... />
```

Auf das img-Tag und ggf. auch outFile-Attribut möchte ich verzichten können.

Hoffe, jetzt ist es etwas klarer geworden. 

Gruß, Titus


----------



## HLX (24. Feb 2009)

Wo ist jetzt das Problem? Du kopierst das Bild im Tag von a nach b sofern es in b nicht vorhanden ist und skalierst es dabei. Wenn das Bild vorhanden ist, zeigst du es mit dem img-Tag an. Sollte alles in deiner Tag-Klasse möglich sein.

Falls das nicht hilft, solltest du den Quellcode deiner Tag-Klasse mal hier reinstellen.

Du kannst Bilder übrigens auch vorab und ohne Programmieraufwand in guter Qualität skalieren. Das Tool IrfanView hat eine sog. Batch-Conversion, die das mit wenigen "Handgriffen" schnell erledigt.


----------



## Marsman (24. Feb 2009)

Okay, ich habe mir mal die Mühe gemacht und den Code auf das wesentliche zu reduzieren:


```
public class ImageTag extends TagSupport {
  
  private String src = null;
  
  private BufferedImage image = null;

  @Override
  public int doStartTag() throws JspException {
    try {
      image = ImageIO.read(new URL(getSrc()).openStream());
    } catch (IOException e) {
      new JspTagException("Image tag: " + e.getMessage());
    }
    return EVAL_BODY_INCLUDE;
  }

  @Override
  public int doEndTag() throws JspException {
    try {
      // image conversion ...
      ServletResponse response = pageContext.getResponse();
      response.setContentType("image/jpeg");
      ImageIO.write(image, "jpeg", response.getOutputStream());
    } catch (IOException e) {
      new JspTagException("Image tag: " + e.getMessage());
    }
    return EVAL_PAGE;
  }

}
```

Ich erhalte folgenden Exception:


```
java.lang.IllegalStateException: getWriter() has already been called for this response
```

Klar. Aber ich will ja auch nicht die Bytes des Bildes als Text, sondern das Bild selbst sehen.

Vielleicht liege ich auch völlig daneben: Aber ich vermute, die Ursache liegt darin, dass der Browser bei dem Standard IMG-Tag einen neue Request absetzen würde, und in diesem dann die Response mit dem ContentType "image/jpeg" zurückgesendet wird. Also eben ein Bild, dass in den HTML-Code eingefügt wird. Wie muss ich das denn in meinem Tag realisieren?

Titus


----------



## HLX (25. Feb 2009)

Wir kommen der Sache näher. Mit dem von dir beschriebenen Code, erreichst du genau das Gegenteil von dem was du willst. Das Senden über den OutputStream unterbindet jegliches Caching, selbst im Browser. Das Bild wird bei jedem Seitenaufruf neu konvertiert und gesendet.

Ich wäre jetzt eher davon ausgegangen, du willst die Datei auf dem Server speichern. In etwa so (nicht getestet):

```
public int doStartTag() throws JspException {
    
    File cacheFile = new File(getCacheFileLocation());
    if(!file.exists()) {
         BufferedImage image = ImageIO.read(new URL(getOriginalSrc()).openStream());
         // Bild hier konvertieren
         OutputStream out = new FileOutputStream(cacheFile);
         ImageIO.write(image, "jpeg", out);
         out.close();
    }
    JspWriter out = pageContext.getOut();
    out.print("<img src='"+getCacheFileLocation()+"'/>"); 
    return SKIP_BODY;
  }
```


```
<xy:myImageTag cacheFileLocation='cacheFolder/cache.jpg' originalSrc='images/myImage.jpg'/>
```


----------



## Marsman (25. Feb 2009)

HLX hat gesagt.:


> Ich wäre jetzt eher davon ausgegangen, du willst die Datei auf dem Server speichern. In etwa so (nicht getestet):



Ich hatte die Vorgehensweise irgendwie zwischendurch aus den Augen verloren: Natürlich schreibt man einfach in IMG-Tag mit der URL zum gecachten Bild. Danke für den Tipp.

Eine Frage hätte ich in dem Zusammenhang allerdings noch: Wie wäre es, wenn man das Bild nun doch nicht zwischenspeichern möchte und es zum Beispiel aus einem Blob stammt. Dann existiert es im Gegensatz zur Variante oben gar nicht auf dem Server. Wie muss ich in meinem Tag dann vorgehen? Geht das auch?

Titus


----------



## maki (25. Feb 2009)

Klar geht das, zB. mit einem Servlet bzw. JSF.


----------



## Marsman (26. Feb 2009)

maki hat gesagt.:


> Klar geht das, zB. mit einem Servlet bzw. JSF.



Jo, ich weiß. Wir ich Eingangs aber geschrieben hatte, möchte ich für diese Anwendung kein JSF verwenden (zu fett) und das Technik mit dem Servlet find ich auch blöd. Deshalb hatte ich ja hier gefragt, ob man nicht doch irgendwie ein eigenständiges Tag schreiben kann?


----------



## HLX (27. Feb 2009)

Tags sollten möglichst ohne Randbedingungen in verschiedenen Umgebungen einsetzbar sein - man spricht ja auch von einer Tag-Bibliothek. Eine Randbedingung ist z.B. deine Datenbankverbindung (für die Abfrage des BLOB). Um nicht davon abhängig zu sein, müsstest du viele Informationen über Attribute anliefern, was sehr hässlich ist. DB-Abfragen haben außerdem m.E. in einem Tag nichts zu suchen - du befindest dich schließlich in der GUI und nicht in der Persistenzebene.

Für dein Anwendungsbeispiel ist ein Servlet die ideale Lösung. Was gefällt dir daran nicht?


----------



## maki (27. Feb 2009)

>> Jo, ich weiß. Wir ich Eingangs aber geschrieben hatte, möchte ich für diese Anwendung kein JSF verwenden (zu fett) und das Technik mit dem Servlet find ich auch blöd. Deshalb hatte ich ja hier gefragt, ob man nicht doch irgendwie ein eigenständiges Tag schreiben kann?

JSF hatte ich vorgeschlagen weil du es abgelehnt hattest *g*
Ne quatsch, dachte du nutzt es bereits.

Wie bereits anfangs gesagt, ein Tag rendert dir höchstens Text in deine HTML Seite, nicht einen Binärendatenstrom welcher ein Bild enthält.
Dieser Text kann ja eine URL für ein img Tag sein dass auf ein Servlet zeigt, welches noch weitere GET Parameter enthält (zB. Skalierung).


----------



## Marsman (28. Feb 2009)

Okay, ich sehe es ein. Wahrscheinlich werde ich es doch die Variante mit dem Servlet anwenden.

Auf die Idee mit dem Image-Tag war ich gekommen, weil ich genau so etwas mit einem Renderer unter JSF schon mal realisiert hatte. JSF verwende ich aber nur noch für Anwendungen, die "inhouse" genutzt werden.

Titus


----------

