Load images from outside of webapps / webcontext / deploy folder using <h:graphicImage> or <img> tag
Asked Answered
T

4

46

I need to display images which reside outside of deploy folder in web application using JSF <h:graphicimage> tag or HTML <img> tag. How can I achieve that?

Tinaret answered 28/12, 2010 at 5:43 Comment(0)
P
82

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:

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

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:

Primalia answered 28/12, 2010 at 5:47 Comment(28)
Hi BalusC,Thanks for your quick reply.If i use it in linux,my absolute path of the image is /home/muneeswaran/apache-tomcat-6.0.29/headers/Aboutus/images/ss.jpg.SO can i store context as <Context docBase="/home/muneeswaran/apache-tomcat-6.0.29/headers" path="/images"/> in server.xml?Tinaret
Yes, then you can access it by example.com/images/Aboutus/images/ss.jpg. However, since you seem to have full control over this (you've placed it in the Tomcat installation folder!), why don't you just put it in /webapps folder? Then you don't need to add another context.Primalia
Hi BalusC,we have already used one context named emsd using servlet, Can i use another one context named as images,If i use this by work according to your advice it makes this context as the subcontext of the esmd.So i can't access the image using <h:graphicimage tag/>.How can i do this?Tinaret
Yes, you can. Just access it the same way.Primalia
Hi BalusC,Thanks for your replies.I will try to make it.Tinaret
@BalusC: My serverlet container is Glassfish3.0.1. Is there a resource folder in glassfish that can be access by h:graphicImage, but untouch by future project redeployment. If not then your 2nd method of creating a new context via docBase sound awesome, but where do I put it. I am using Glassfish, do I put it in web.xml?Silvie
@Harry: create a Virtual Directory in asadmin or by sun-web.xml. marceble.com/2009/07/virtual-directories-in-glassfishPrimalia
@BalusC: btw, the full path to the image is /Users/KingdomHeart/resources/scholar/network/1.jpg. So I put in my sun-web.xml the following <property name=”networkDocroot″ value=”from=/network/* dir=/Users/KingdomHeart/resources/scholar/” />. How do I access the image via h:graphicImage. I try <h:graphicImage value="/network/1.jpg" />, but no luck.Silvie
@Harry: Are those quotes copypasted? Curly quotes are invalid in XML. I wonder if the server didn't throw XML parsing error. If the quotes are fine and it still doesn't work, then sorry, can't help much. I would need to play around it myself first (which I haven't done yet on Glassfish).Primalia
The quotes was in fact causing deployment error, but I fixed it. From what I did above, do I suppose to be able to access the image via the web as well, like via localhost:8080/myproject-war/network/1.jpg. As it right now, I cantSilvie
Apparent, the name of the docroot have to follow a naming convention like this (alternatedocroot_1, alternatedocroot_2 ...). It work now. Thank youSilvie
@Primalia - what is the recommended way between adding <context< and creating Servlet?Luettaluevano
@Odelya: depends on whether you have full control over the server and/or want to have more fine grained control over serving the resource than possible with the servletcontainer's builtin default servlet.Primalia
@Odelya: If you have full control over server config, go ahead with context. If you don't have or are not satifsied with behaviour of the default servlet, go ahead with own servlet.Primalia
@BalusC, I tried the same thing for video files and it does not display them correctly. should the servlet be different?Luettaluevano
@Odelya: Please describe "does not display them correctly" in more detail. Are the response headers correct? Importantingly, the content type. If the content type is wrong, you've likely to add a new mime mapping for the file extension to the servletcontainer's web.xml.Primalia
The problem is that the <video> cursor cannot be played with, while if I locate the movie in web app folder, I can play with the cursor. the response header are: response.setBufferSize(DEFAULT_BUFFER_SIZE); response.setContentType(contentType); //response.setHeader("Content-Length", String.valueOf(basePath.getContent().getSize())); response.setHeader("Content-Disposition", "inline; filename=\"" + baseName + "\"");Luettaluevano
@BalusC, How would you this in Jboss AS 7?Forecast
@jack: stackoverflow.com/questions/9468045/… Don't pay too much attention to the JBoss 4 screenshot, the system property jboss.server.data.dir is still the same.Primalia
@Primalia not exactly... I want option 2Forecast
@jacktrades: oh sorry, can't tell from top of head, never done that in JBoss 7. Will maybe investigate later. Now first BBQ time ;)Primalia
@Primalia is your article still up to date for Java EE 6?Forecast
@jacktrades: sorry, I'm not interested in chat. Coming back to the subject, this may give clues jojovedder.blogspot.com/2012/11/…Primalia
@Primalia saw that post, check out my last questionForecast
@Primalia my <host> tag in tomcat's server.xml overwrite my file. I'm running from eclispe. I added <Context docBase="/path/to/images" path="/images" /> but when I run tomcat, it was overwritten and my added tag is removed.Premonition
@YeWint: if you let Eclipse take control of Tomcat, then you should edit the server.xml in Eclipse's Servers project instead.Primalia
Awesome tips..I followed the step five and worked fine for me . Thank you very much.Volnay
forum.primefaces.org/…. Only worked with Omnifaces: 'o:graphicImage' (@balusc)Charil
G
2

In order to achieve what you need using <h:graphicImage> or <img> tags, you require to create a Tomcat v7 alias in order to map the external path to your web app's context.

To do so, you will need to specify your web app's context. The easiest would be to define a META-INF/context.xml file with the following content:

<Context path="/myapp" aliases="/images=/path/to/external/images">
</Context>

Then after restarting your Tomcat server, you can access your images files using <h:graphicImage> or <img> tags as following:

<h:graphicImage value="/images/my-image.png">

or

<img src="/myapp/images/my-image.png">

*Note the context path is necessary for the tag but not for the


Another possible approach if you don't require the images to be available through HTTP GET method, could be to use Primefaces <p:fileDownload> tag (using commandLink or commandButton tags - HTTP POST method).

In your Facelet:

<h:form>
  <h:commandLink id="downloadLink" value="Download">  
    <p:fileDownload value="#{fileDownloader.getStream(file.path)}" />  
</h:commandLink>
</h:form

In your 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;
       }
    }
}
Grimbald answered 30/4, 2013 at 5:18 Comment(0)
A
2

In PrimeFaces you can implement your bean in this way:

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

In your HTML Facelet:

<body onload="#{yourBean.prepImage()}"></body> 
<p:graphicImage value="#{youyBean.image}" style="width:100%;height:100%" cache="false" >
</p:graphicImage>

I suggest to set the attribute cache="false" in the graphicImage component.

Antinucleon answered 12/6, 2018 at 21:57 Comment(0)
A
-2

In JSP

<img src="data:image/jpeg;base64,
<%= new String(Base64.encode(Files.readAllBytes(Paths.get("C:\\temp\\A.jpg"))))%>"/>

Packages are com.sun.jersey.core.util.Base64, java.nio.file.Paths and java.nio.file.Files.

Alhambra answered 15/12, 2014 at 15:32 Comment(2)
we won't in JSF not JSPAdal
How is this possible in JSF?Bourse

© 2022 - 2024 — McMap. All rights reserved.