J'ai besoin d'afficher des images situées en dehors du dossier deploy dans une application Web à l'aide des balises JSF <h:graphicimage>
ou HTML <img>
. Comment puis-je y arriver?
Il doit être accessible via une URL publique. Ainsi, le <img src>
doit en fin de compte faire référence à un URI http://
, et non à quelque chose comme un URI file://
ou autre. En fin de compte, la source HTML est exécutée sur la machine de l'utilisateur final et les images sont téléchargées individuellement par le navigateur Web lors de l'analyse syntaxique de la source HTML. Lorsque le navigateur Web rencontre un URI file://
tel que C:\path\to\image.png
, il recherchera l'image dans le système de fichiers du disque local de l'utilisateur, à la place de celle du serveur Web. Cela ne fonctionnera évidemment pas si le navigateur Web s'exécute sur une machine physiquement différente de celle du serveur Web.
Il y a plusieurs façons d'y parvenir:
Si vous avez un contrôle total sur le dossier des images, déposez-le simplement avec toutes les images, par exemple. /images
directement dans le dossier deploy de servletcontainer, tel que le dossier /webapps
dans le cas de Tomcat et le dossier /domains/domain1/applications
dans le cas de GlassFish. Aucune autre configuration n'est nécessaire.
Ou, ajoutez un nouveau contexte d'application Web au serveur qui pointe vers l'emplacement du système de fichiers du disque absolu du dossier contenant ces images. Comment faire cela dépend du conteneur utilisé. Les exemples ci-dessous supposent que les images se trouvent dans /path/to/images
et que vous souhaitez y accéder via http: //.../images .
Dans le cas de Tomcat, ajoutez la nouvelle entrée suivante au /conf/server.xml
de Tomcat dans <Host>
:
<Context docBase="/path/to/images" path="/images" />
Dans le cas de GlassFish, ajoutez l'entrée suivante à /WEB-INF/glassfish-web.xml
:
<property name="alternatedocroot_1" value="from=/images/* dir=/path/to" />
Dans le cas de WildFly, ajoutez l'entrée suivante dans <Host name="default-Host">
de /standalone/configuration/standalone.xml
...
<location name="/images" handler="images-content" />
... et plus bas dans <handlers>
entrée du même <subsystem>
qu'au-dessus <location>
:
<file name="images-content" path="/path/to/images" />
Ou, créez une Servlet
qui diffuse l'image du disque vers la réponse:
@WebServlet("/images/*")
public class ImageServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String filename = request.getPathInfo().substring(1);
File file = new File("/path/to/images", filename);
response.setHeader("Content-Type", getServletContext().getMimeType(filename));
response.setHeader("Content-Length", String.valueOf(file.length()));
response.setHeader("Content-Disposition", "inline; filename=\"" + filename + "\"");
Files.copy(file.toPath(), response.getOutputStream());
}
}
Si vous utilisez des OmniFaces, alors la FileServlet
peut être utile car elle prend également en compte les demandes d'en-tête, de mise en cache et de plage.
Ou bien, utilisez OmniFaces <o:graphicImage>
qui prend en charge une propriété de bean renvoyant byte[]
ou InputStream
:
@Named
@ApplicationScoped
public class Bean {
public InputStream getImage(String filename) {
return new FileInputStream(new File("/path/to/images", filename));
}
}
Ou bien, utilisez PrimeFaces <p:graphicImage>
qui prend en charge une méthode de bean renvoyant la variable StreamedContent
propre à PrimeFaces.
@Named
@ApplicationScoped
public class Bean {
public StreamedContent getImage() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
// So, we're rendering the view. Return a stub StreamedContent so that it will generate right URL.
return new DefaultStreamedContent();
}
else {
// So, browser is requesting the image. Return a real StreamedContent with the image bytes.
String filename = context.getExternalContext().getRequestParameterMap().get("filename");
return new DefaultStreamedContent(new FileInputStream(new File("/path/to/images", filename)));
}
}
}
Pour la première manière et les approches Tomcat et WildFly de la deuxième manière, les images seront disponibles par http://example.com/images/filename.ext et seront donc référencées en HTML pur comme suit
<img src="/images/filename.ext" />
Pour l’approche GlassFish en deuxième et troisième voies, les images seront disponibles par http://example.com/context/images/nomfichier.ext et seront donc référencées en HTML simple comme suit
<img src="#{request.contextPath}/images/filename.ext" />
ou dans JSF comme suit (le chemin de contexte est automatiquement ajouté au début)
<h:graphicImage value="/images/filename.ext" />
Pour l’approche OmniFaces de la quatrième manière, référencez-la comme suit
<o:graphicImage value="#{bean.getImage('filename.ext')}" />
Pour l’approche PrimeFaces de la cinquième manière, référencez-la comme suit:
<p:graphicImage value="#{bean.image}">
<f:param name="filename" value="filename.ext" />
</p:graphicImage>
Notez que l'exemple #{bean}
est @ApplicationScoped
car il représente essentiellement un service sans état. Vous pouvez également le créer @RequestScoped
, mais le bean serait alors recréé à chaque demande, pour rien. Vous ne pouvez pas le créer @ViewScoped
car, pour le moment, le navigateur doit télécharger l'image, le serveur ne crée pas de page JSF. Vous pouvez le rendre @SessionScoped
, mais ensuite, il est enregistré dans la mémoire, pour rien.
Pour obtenir ce dont vous avez besoin en utilisant les balises <h:graphicImage>
ou <img>
, vous devez créer un alias Tomcat v7 afin de mapper le chemin externe au contexte de votre application Web.
Pour ce faire, vous devrez spécifier le contexte de votre application Web . Le plus simple serait de définir un fichier META-INF/context.xml avec le contenu suivant:
<Context path="/myapp" aliases="/images=/path/to/external/images">
</Context>
Ensuite, après avoir redémarré votre serveur Tomcat, vous pouvez accéder à vos fichiers images à l’aide des balises <h:graphicImage
> ou <img>
comme suit:
<h:graphicImage value="/images/my-image.png">
ou
<img src="/myapp/images/my-image.png">
* Notez que le chemin de contexte est nécessaire pour la balise mais pas pour la
Une autre approche possible si vous n’exigez pas que les images soient disponibles via la méthode HTTP GET pourrait être d’utiliser Primefaces <p:fileDownload>
tag (à l’aide de balises commandLink ou commandButton - HTTP POST méthode).
Dans votre facelet:
<h:form>
<h:commandLink id="downloadLink" value="Download">
<p:fileDownload value="#{fileDownloader.getStream(file.path)}" />
</h:commandLink>
</h:form
Dans ta fève:
@ManagedBean
@ApplicationScope
public class FileDownloader {
public StreamedContent getStream(String absPath) throws Exception {
FileInputStream fis = new FileInputStream(absPath);
BufferedInputStream bis = new BufferedInputStream(fis);
StreamedContent content = new DefaultStreamedContent(bis);
return content;
}
}
}
Dans PrimeFaces, vous pouvez implémenter votre bean de la manière suivante:
private StreamedContent image;
public void setImage(StreamedContent image) {
this.image = image;
}
public StreamedContent getImage() throws Exception {
return image;
}
public void prepImage() throws Exception {
File file = new File("/path/to/your/image.png");
InputStream input = new FileInputStream(file);
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
setImage(new DefaultStreamedContent(input,externalContext.getMimeType(file.getName()), file.getName()));
}
Dans votre HTML Facelet:
<body onload="#{yourBean.prepImage()}"></body>
<p:graphicImage value="#{youyBean.image}" style="width:100%;height:100%" cache="false" >
</p:graphicImage>
Je suggère de définir l'attribut cache = "false" dans le composant graphicImage.