使用<h:graphicImage>或<img>标签从webapps / webcontext / deploy文件夹外加载图像

46
我需要在 Web 应用程序中使用 JSF 的 <h:graphicimage> 标签或 HTML 的 <img> 标签来显示位于部署文件夹外的图像。我该如何实现?
4个回答

82
简洁明了地说,它必须可以通过公共URL访问。因此,<img src> 最终必须引用 http:// URI,而不是像 file:// URI 这样的东西。最终,HTML源代码在最终用户的计算机上执行,Web浏览器在解析HTML源代码期间单独下载图像。当Web浏览器遇到像 C:\path\to\image.png 这样的 file:// URI 时,它将在最终用户自己的本地磁盘文件系统中查找图像,而不是在Web服务器中查找。如果Web浏览器运行在与Web服务器物理上不同的计算机上,则显然无法正常工作。
有几种方法可以实现这一点:
  1. 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.


  2. 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" />
    

  3. 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.


  4. Or, use OmniFaces <o:graphicImage> which supports a bean property returning byte[] or InputStream:

    @Named
    @ApplicationScoped
    public class Bean {
    
        public InputStream getImage(String filename) {
            return new FileInputStream(new File("/path/to/images", filename));
        }
    }
    

  5. Or, use PrimeFaces <p:graphicImage> which supports a bean method returning PrimeFaces-specific StreamedContent.

    @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)));
            }
        }
    }
    

对于第一种方法和第二种方法中的Tomcat和WildFly方法,图像将通过http://example.com/images/filename.ext可用,并且可以在普通HTML中引用,如下所示

<img src="/images/filename.ext" />

对于第二种和第三种方式中的GlassFish方法,图像将通过http://example.com/context/images/filename.ext可用,并且可以在普通HTML中引用,如下所示

<img src="#{request.contextPath}/images/filename.ext" />

或者在JSF中按照以下方式进行(上下文路径会自动添加)

<h:graphicImage value="/images/filename.ext" />

在第四种方法中,使用以下方式引用OmniFaces方法:
<o:graphicImage value="#{bean.getImage('filename.ext')}" />

对于第五种方法中的PrimeFaces方法,请按以下方式引用:

<p:graphicImage value="#{bean.image}">
    <f:param name="filename" value="filename.ext" />
</p:graphicImage>

请注意,示例#{bean}@ApplicationScoped,因为它基本上代表了一个无状态服务。您也可以将其设置为@RequestScoped,但那么该bean将在每个请求中重新创建,没有任何意义。您不能将其设置为@ViewScoped,因为在浏览器需要下载图像的时候,服务器不会创建JSF页面。您可以将其设置为@SessionScoped,但那么它将保存在内存中,没有任何意义。
另请参阅:

1
@Harry:在asadmin或通过sun-web.xml创建虚拟目录。http://www.marceble.com/2009/07/virtual-directories-in-glassfish/ - BalusC
@BalusC- 在添加<context>和创建Servlet之间,哪种方式是推荐的? - Dejell
@Odelya:这取决于您是否对服务器拥有完全控制权,以及是否希望比使用servlet容器的内置默认servlet更细粒度地控制资源的提供。 - BalusC
如果您完全控制服务器配置,请使用上下文。如果您没有或不满意默认servlet的行为,请使用自己的servlet。 - BalusC
@BalusC,我尝试对视频文件做同样的事情,但它们无法正确显示。这个servlet应该不一样吗? - Dejell
显示剩余23条评论

2
在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"。

2
为了使用 <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">

*注意,对于 <img> 标签来说,上下文路径是必须的,但对于

标签来说则不是。


如果您不需要通过HTTP GET方法获取图像,则另一个可能的方法是使用Primefaces的<p:fileDownload>标签(使用commandLinkcommandButton标签 - 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;
       }
    }
}

-2

在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.Pathsjava.nio.file.Files


我们不会使用JSF而不是JSP。 - Divyesh Kanzariya
在JSF中如何实现这个? - internet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接