我需要使用 JSF<h:graphicimage>
标记或 HTML<img>
标记显示驻留在 Web 应用程序中部署文件夹之外的图像。我怎样才能做到这一点?
4 回答
To the point, it has to be accessible by a public URL. Thus, the <img src>
must ultimately refer a http://
URI, not something like a file://
URI or so. Ultimately, the HTML source is executed at enduser's machine and images are downloaded individually by the webbrowser during parsing the HTML source. When the webbrowser encounters a file://
URI such as C:\path\to\image.png
, then it will look in enduser's own local disk file system for the image instead of the webserver's one. This is obviously not going to work if the webbrowser runs at a physically different machine than the webserver.
There are several ways to achieve this:
If you have full control over the images folder, then just drop the folder with all images, e.g.
/images
directly in servletcontainer's deploy folder, such as the/webapps
folder in case of Tomcat and/domains/domain1/applications
folder in case of GlassFish. No further configuration is necessary.Or, add a new webapp context to the server which points to the absolute disk file system location of the folder with those images. How to do that depends on the container used. The below examples assume that images are located in
/path/to/images
and that you'd like to access them via http://.../images.In case of Tomcat, add the following new entry to Tomcat's
/conf/server.xml
inside<Host>
:<Context docBase="/path/to/images" path="/images" />
In case of GlassFish, add the following entry to
/WEB-INF/glassfish-web.xml
:<property name="alternatedocroot_1" value="from=/images/* dir=/path/to" />
In case of WildFly, add the following entry inside
<host name="default-host">
of/standalone/configuration/standalone.xml
...<location name="/images" handler="images-content" />
... and further down in
<handlers>
entry of the very same<subsystem>
as above<location>
:<file name="images-content" path="/path/to/images" />
Or, create a
Servlet
which streams the image from disk to response:@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()); } }
If you happen to use OmniFaces, then the
FileServlet
may be useful as it also takes into account head, caching and range requests.Or, use OmniFaces
<o:graphicImage>
which supports a bean property returningbyte[]
orInputStream
:@Named @ApplicationScoped public class Bean { public InputStream getImage(String filename) { return new FileInputStream(new File("/path/to/images", filename)); } }
Or, use PrimeFaces
<p:graphicImage>
which supports a bean method returning PrimeFaces-specificStreamedContent
.@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))); } } }
For the first way and the Tomcat and WildFly approaches in second way, the images will be available by http://example.com/images/filename.ext and thus referencable in plain HTML as follows
<img src="/images/filename.ext" />
For the GlassFish approach in second way and the third way, the images will be available by http://example.com/context/images/filename.ext and thus referencable in plain HTML as follows
<img src="#{request.contextPath}/images/filename.ext" />
or in JSF as follows (context path is automatically prepended)
<h:graphicImage value="/images/filename.ext" />
For the OmniFaces approach in fourth way, reference it as follows
<o:graphicImage value="#{bean.getImage('filename.ext')}" />
For the PrimeFaces approach in fifth way, reference it as follows:
<p:graphicImage value="#{bean.image}">
<f:param name="filename" value="filename.ext" />
</p:graphicImage>
Note that the example #{bean}
is @ApplicationScoped
as it basically represents a stateless service. You can also make it @RequestScoped
, but then the bean would be recreated on every single request, for nothing. You cannot make it @ViewScoped
, because at the moment the browser needs to download the image, the server doesn't create a JSF page. You can make it @SessionScoped
, but then it's saved in memory, for nothing.
See also:
- Recommended way to save uploaded files in a servlet application
- Simplest way to serve static data from outside the application server in a Java web application
- Abstract template for a static resource servlet (supporting HTTP caching)
- Show image as byte[] from database as graphic image in JSF page
- Display dynamic image from database with p:graphicImage and StreamedContent
- How to choose the right bean scope?
为了实现您需要使用<h:graphicImage>
或<img>
标记的内容,您需要创建一个 Tomcat v7 别名,以便将外部路径映射到您的 Web 应用程序的上下文。
为此,您需要指定 Web 应用程序的上下文。最简单的方法是定义一个包含以下内容的 META-INF/context.xml 文件:
<Context path="/myapp" aliases="/images=/path/to/external/images">
</Context>
然后在重新启动 Tomcat 服务器后,您可以使用<h:graphicImage
> 或<img>
标签访问图像文件,如下所示:
<h:graphicImage value="/images/my-image.png">
或者
<img src="/myapp/images/my-image.png">
*注意上下文路径是标签所必需的,但不是
如果您不要求图像通过 HTTP GET 方法可用,另一种可能的方法是使用 Primefaces<p:fileDownload>
标签(使用commandLink或commandButton标签 - HTTP POST 方法)。
在你的 Facelet 中:
<h:form>
<h:commandLink id="downloadLink" value="Download">
<p:fileDownload value="#{fileDownloader.getStream(file.path)}" />
</h:commandLink>
</h:form
在你的 bean 中:
@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;
}
}
}
在 PrimeFaces 中,您可以通过以下方式实现您的 bean:
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()));
}
在您的 HTML Facelet 中:
<body onload="#{yourBean.prepImage()}"></body>
<p:graphicImage value="#{youyBean.image}" style="width:100%;height:100%" cache="false" >
</p:graphicImage>
我建议在graphicImage组件中设置属性cache="false"。
在 JSP 中
<img src="data:image/jpeg;base64,
<%= new String(Base64.encode(Files.readAllBytes(Paths.get("C:\\temp\\A.jpg"))))%>"/>
包com.sun.jersey.core.util.Base64
是java.nio.file.Paths
和java.nio.file.Files
。