5

I have a servlet deployed under http://ip:8080/simple
The servlet is under package a.b.c
I have an html page in a.b.resources named Test.html.

The html has an img tag for an image.

In the servlet I do:

htmlFile = MyServlet.class.getResourceAsStream("/a/b/resources/Test.html");
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
byte[] bytes=new byte[htmlFile.available()];
htmlFile.read(bytes);
resp.setContentLength(bytes.length);
writer.print(new String(bytes));
writer.flush();
writer.close();

The html page appears on the browser but in the place of the image I see its alt description.
I have tried:

<img alt="Company A" src="./CompanyLogo.jpg">

<img alt="Company A" src="/a/b/resources/CompanyLogo.jpg">

<img alt="Company A" src="CompanyLogo.jpg">

But none of these works.
The jpg image is under /a/b/c/resources i.e. in the same directory as the HTML page.
I am using embedded Jetty.

What am I messing here?

Cratylus
  • 52,998
  • 69
  • 209
  • 339

3 Answers3

7

The browser is trying to resolve those resources relative to the current request URI (as you see in browser address bar). Those resources of course does not exist in your public web content as you seem to have placed them in the classpath.

In order to solve this, you would really need to parse the HTML and change all domain-relative src and/or href attributes of <a>, <img>, <base>, <link>, <script>, <iframe>, etc elements to let them point to a servlet which streams those resources from the classpath to the HTTP response.

It's a bit of work, but Jsoup makes it easy. Here's an example which assumes that your servlet is mapped on an URL pattern of /proxy/*.

String proxyURL = request.getContextPath() + "/proxy/";
InputStream input = MyServlet.class.getResourceAsStream("/a/b/resources" + request.getPathInfo());

if (request.getRequestURI().endsWith(".html")) { // A HTML page is been requested.
    Document document = Jsoup.parse(input, "UTF-8", null);

    for (Element element : document.select("[href]")) {
        element.attr("href", proxyURL + element.attr("href"));
    }

    for (Element element : document.select("[src]")) {
        element.attr("src", proxyURL + element.attr("src"));
    }

    response.setContentType("text/html;charset=UTF-8");
    response.setCharacterEncoding("UTF-8");
    response.getWriter().write(document.html());
}
else { // Other resources like images, etc which have been proxied to this servlet.
    response.setContentType(getServletContext().getMimeType(request.getPathInfo()));
    OutputStream output = response.getOutputStream();
    byte[] buffer = new byte[8192];

    for (int length = 0; (length = input.read(buffer)) > 0;) {
        output.write(buffer, 0, length);
    }
}

input.close();

Open it by http://yourdomain:yourport/contextname/proxy/test.html.

Mohsen
  • 3,512
  • 3
  • 38
  • 66
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • +1.Thank you for the detailed answer.To be honest I feel like an idiot.My intention was to send a forbidden page with an image of a logo in one specific instance.I thought that was the fastest approach (since I think in Jetty it requires more jars to use jsp).It seems I took the wrong turn – Cratylus Sep 12 '11 at 15:56
  • Regardless, I would rather put those resources in public web content. This all is pretty clumsy :) – BalusC Sep 12 '11 at 15:58
  • In embedded jetty, not really sure where is the public web content to place them – Cratylus Sep 12 '11 at 16:29
  • Add it as `WebAppContext`. See also Jetty's own tutorial: http://wiki.eclipse.org/Jetty/Tutorial/Embedding_Jetty – BalusC Sep 12 '11 at 16:32
1

There is no way to do this without implementing a servlet that will read the image out of the resources file. Try this:

public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
  byte[] bbuf = new byte[8192];
  resp.setContentType(req.getSession().getServletContext().getMimeType( req.getPathInfo()));
  InputStream in = MyImageServlet.class.getResourceAsStream("/"+req.getPathInfo());
  OutputStream op = resp.getOutputStream();
  int length;
  while ((in != null) && ((length = in.read(bbuf)) != -1)){
      op.write(bbuf,0,length);
      op.flush();
  }
  in.close();
  op.close();  
}

then register it in your web.xml like so

<servlet-mapping>
  <servlet-name>fetchimage</servlet-name>
  <url-pattern>/fetchimage/*</url-pattern>
</servlet-mapping>

and then use it like so

<img alt="Company A" src="/fetchimage/a/b/resources/CompanyLogo.jpg">

You WILL need to implement a lot of error checking (A LOT OF ERROR CHECKING, just to clarify :)), filter the paths to make sure that someone can't just read your class files using the same technique, but some variation on this should work for you.

Femi
  • 64,273
  • 8
  • 118
  • 148
  • Really?I thought this was fast way.But the browser gets an html page.Doesn't the browser use the `src` tag to request the image? – Cratylus Sep 11 '11 at 19:36
  • Yes, but the standard servlet file handlers do not introspect into all your jars looking for files. If you want to be *fast* copy all your images into the top-level directory for your project. – Femi Sep 11 '11 at 19:40
0

request.getRequestDispatcher("/a/b/Test.html").forward(request, response);

Shivan Dragon
  • 15,004
  • 9
  • 62
  • 103
  • http://download.oracle.com/docs/cd/E17802_01/products/products/servlet/2.3/javadoc/javax/servlet/RequestDispatcher.html#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse). Forwards a request from a servlet to another resource (servlet, JSP file, or HTML file). If your html page is packaged in your war at the location /a/b/Test.html then it will work. – Shivan Dragon Sep 11 '11 at 21:02
  • May be it is because of Jetty – Cratylus Sep 11 '11 at 21:03
  • It's not, I used this multiple times. I have a GAE/GWT project that also includes servlets that do this. Code in my servlet: request.getRequestDispatcher("/JavakataPage.html").forward(request, response);. Location of html file in war: in the root of the war file. I'm doing this with Servlet API version 2.4. Trying it on jetty works ok. – Shivan Dragon Sep 11 '11 at 21:08
  • When I do this it seems like it goes into a loop and keeps calling forward indefinetly.Eventually I get an exception. – Cratylus Sep 11 '11 at 21:16
  • Well you can try response.sendRedirect("http://full_public_url_of_Test.html"); but that's not really the same thing at all... – Shivan Dragon Sep 12 '11 at 07:18