Simplest way to serve static data from outside the

2018-12-31 02:47发布

I have a Java web application running on Tomcat. I want to load static images that will be shown both on the Web UI and in PDF files generated by the application. Also new images will be added and saved by uploading via the Web UI.

It's not a problem to do this by having the static data stored within the the web container but storing and loading them from outside the web container is giving me headache.

I'd prefer not to use a separate web server like Apache for serving the static data at this point. I also don't like the idea of storing the images in binary in a database.

I've seen some suggestions like having the image directory being a symbolic link pointing to a directory outside the web container, but will this approach work both on Windows and *nix environments?

Some suggest writing a filter or a servlet for handling the image serving but those suggestions have been very vague and high-level without pointers to more detailed information on how to accomplish this.

10条回答
冷夜・残月
2楼-- · 2018-12-31 03:02

I've seen some suggestions like having the image directory being a symbolic link pointing to a directory outside the web container, but will this approach work both on Windows and *nix environments?

If you adhere the *nix filesystem path rules (i.e. you use exclusively forward slashes as in /path/to/files), then it will work on Windows as well without the need to fiddle around with ugly File.separator string-concatenations. It would however only be scanned on the same working disk as from where this command is been invoked. So if Tomcat is for example installed on C: then the /path/to/files would actually point to C:\path\to\files.

If the files are all located outside the webapp, and you want to have Tomcat's DefaultServlet to handle them, then all you basically need to do in Tomcat is to add the following Context element to /conf/server.xml inside <Host> tag:

<Context docBase="/path/to/files" path="/files" />

This way they'll be accessible through http://example.com/files/.... GlassFish/Payara configuration example can be found here and WildFly configuration example can be found here.

If you want to have control over reading/writing files yourself, then you need to create a Servlet for this which basically just gets an InputStream of the file in flavor of for example FileInputStream and writes it to the OutputStream of the HttpServletResponse.

On the response, you should set the Content-Type header so that the client knows which application to associate with the provided file. And, you should set the Content-Length header so that the client can calculate the download progress, otherwise it will be unknown. And, you should set the Content-Disposition header to attachment if you want a Save As dialog, otherwise the client will attempt to display it inline. Finally just write the file content to the response output stream.

Here's a basic example of such a servlet:

@WebServlet("/files/*")
public class FileServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        String filename = URLDecoder.decode(request.getPathInfo().substring(1), "UTF-8");
        File file = new File("/path/to/files", filename);
        response.setHeader("Content-Type", getServletContext().getMimeType(filename));
        response.setHeader("Content-Length", String.valueOf(file.length()));
        response.setHeader("Content-Disposition", "inline; filename=\"" + file.getName() + "\"");
        Files.copy(file.toPath(), response.getOutputStream());
    }

}

When mapped on an url-pattern of for example /files/*, then you can call it by http://example.com/files/image.png. This way you can have more control over the requests than the DefaultServlet does, such as providing a default image (i.e. if (!file.exists()) file = new File("/path/to/files", "404.gif") or so). Also using the request.getPathInfo() is preferred above request.getParameter() because it is more SEO friendly and otherwise IE won't pick the correct filename during Save As.

You can reuse the same logic for serving files from database. Simply replace new FileInputStream() by ResultSet#getInputStream().

Hope this helps.

See also:

查看更多
琉璃瓶的回忆
3楼-- · 2018-12-31 03:04

You can do it by putting your images on a fixed path (for example: /var/images, or c:\images), add a setting in your application settings (represented in my example by the Settings.class), and load them like that, in a HttpServlet of yours:

String filename = Settings.getValue("images.path") + request.getParameter("imageName")
FileInputStream fis = new FileInputStream(filename);

int b = 0;
while ((b = fis.read()) != -1) {
        response.getOutputStream().write(b);
}

Or if you want to manipulate the image:

String filename = Settings.getValue("images.path") + request.getParameter("imageName")
File imageFile = new File(filename);
BufferedImage image = ImageIO.read(imageFile);
ImageIO.write(image, "image/png", response.getOutputStream());

then the html code would be <img src="imageServlet?imageName=myimage.png" />

Of course you should think of serving different content types - "image/jpeg", for example based on the file extension. Also you should provide some caching.

In addition you could use this servlet for quality rescaling of your images, by providing width and height parameters as arguments, and using image.getScaledInstance(w, h, Image.SCALE_SMOOTH), considering performance, of course.

查看更多
回忆,回不去的记忆
4楼-- · 2018-12-31 03:10

Requirement : Accessing the static Resources (images/videos., etc.,) from outside of WEBROOT directory or from local disk

Step 1 :
Create a folder under webapps of tomcat server., let us say the folder name is myproj

Step 2 :
Under myproj create a WEB-INF folder under this create a simple web.xml

code under web.xml

<web-app>
</web-app>

Directory Structure for the above two steps

c:\programfile\apachesoftwarefoundation\tomcat\...\webapps
                                                            |
                                                            |---myproj
                                                            |   |
                                                            |   |---WEB-INF
                                                                |   |
                                                                    |---web.xml

Step 3:
Now create a xml file with name myproj.xml under the following location

c:\programfile\apachesoftwarefoundation\tomcat\conf\catalina\localhost

CODE in myproj.xml:

<Context path="/myproj/images" docBase="e:/myproj/" crossContext="false" debug="0" reloadable="true" privileged="true" /> 

Step 4:
4 A) Now create a folder with name myproj in E drive of your hard disk and create a new

folder with name images and place some images in images folder (e:myproj\images\)

Let us suppose myfoto.jpg is placed under e:\myproj\images\myfoto.jpg

4 B) Now create a folder with name WEB-INF in e:\myproj\WEB-INF and create a web.xml in WEB-INF folder

Code in web.xml

<web-app>
</web-app>

Step 5:
Now create a .html document with name index.html and place under e:\myproj

CODE under index.html Welcome to Myproj

The Directory Structure for the above Step 4 and Step 5 is as follows

E:\myproj
    |--index.html
    |
    |--images
    |     |----myfoto.jpg
    |
    |--WEB-INF
    |     |--web.xml

Step 6:
Now start the apache tomcat server

Step 7:
open the browser and type the url as follows

http://localhost:8080/myproj    

then u display the content which is provided in index.html

Step 8:
To Access the Images under your local hard disk (outside of webroot)

http://localhost:8080/myproj/images/myfoto.jpg
查看更多
孤独寂梦人
5楼-- · 2018-12-31 03:13

If you decide to dispatch to FileServlet then you will also need allowLinking="true" in context.xml in order to allow FileServlet to traverse the symlinks.

See http://tomcat.apache.org/tomcat-6.0-doc/config/context.html

查看更多
唯独是你
6楼-- · 2018-12-31 03:14

Add to server.xml :

 <Context docBase="c:/dirtoshare" path="/dir" />

Enable dir file listing parameter in web.xml :

    <init-param>
        <param-name>listings</param-name>
        <param-value>true</param-value>
    </init-param>
查看更多
深知你不懂我心
7楼-- · 2018-12-31 03:15

Read the InputStream of a file and write it to ServletOutputStream for sending binary data to the client.

  • Local file You can read a file directly using FileInputStream('path/image.png').
  • Mongo DataBase file's you can get InputStream using GridFS.
@WebServlet("/files/URLStream")
public class URLStream extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public URLStream() {
        super();
    }

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        File source = new File("D:\\SVN_Commit.PNG");
        long start = System.nanoTime();

        InputStream image = new FileInputStream(source);

        /*String fileID = request.getParameter("id");
        System.out.println("Requested File ID : "+fileID);
        // Mongo DB GridFS - https://stackoverflow.com/a/33544285/5081877
        image = outputImageFile.getInputStream();*/

        if( image != null ) {
            BufferedInputStream bin = null;
            BufferedOutputStream bout = null;
            ServletOutputStream sos = response.getOutputStream();
            try {
                bin = new BufferedInputStream( image );
                bout = new BufferedOutputStream( sos );
                int ch =0; ;
                while((ch=bin.read())!=-1) {
                    bout.write(ch);
                }
            } finally {
                bin.close();
                image.close();
                bout.close();
                sos.close();
            }

        } else {
            PrintWriter writer = response.getWriter();
            writer.append("Something went wrong with your request.");
            System.out.println("Image not available.");
        }
        System.out.println("Time taken by Stream Copy = "+(System.nanoTime()-start));
    }
}

Result the URL directly to the src attibute.

<img src='http://172.0.0.1:8080/ServletApp/files/URLStream?id=5a575be200c117cc2500003b' alt="mongodb File"/>
<img src='http://172.0.0.1:8080/ServletApp/files/URLStream' alt="local file"/>

<video controls="controls" src="http://172.0.0.1:8080/ServletApp/files/URLStream"></video>
查看更多
登录 后发表回答