在Java Web应用程序中从应用程序服务器外部提供静态数据的最简单方法

时间:2009-11-28 10:58:39

标签: tomcat java-ee servlets static-content

我有一个在Tomcat上运行的Java Web应用程序。我想加载静态图像,这些图像将在Web UI和应用程序生成的PDF文件中显示。此外,还将通过Web UI上传添加和保存新图像。

通过将静态数据存储在Web容器中但从Web容器外部存储和加载它们来解决这个问题并不是一个问题。

我不想在此时使用像Apache这样的单独的Web服务器来提供静态数据。我也不喜欢将图像以二进制形式存储在数据库中。

我见过一些建议,比如将图像目录作为指向Web容器外部目录的符号链接,但这种方法是否适用于Windows和* nix环境?

有人建议编写一个过滤器或servlet来处理图像服务,但这些建议非常模糊和高级别,没有指向如何实现这一目标的更详细信息。

10 个答案:

答案 0 :(得分:153)

  

我见过一些建议,比如将图像目录作为指向Web容器外部目录的符号链接,但这种方法是否适用于Windows和* nix环境?

如果您遵守* nix文件系统路径规则(即您在/path/to/files中使用专门的正斜杠),那么它也可以在Windows上运行,而无需摆弄丑陋的File.separator字符串-concatenations。但是,它只能在与调用此命令的位置相同的工作磁盘上进行扫描。因此,如果C:上安装了Tomcat,那么/path/to/files实际上会指向C:\path\to\files

如果这些文件都位于webapp之外,并且您希望Tomcat的DefaultServlet能够处理它们,那么您在Tomcat中基本上需要做的就是将以下Context元素添加到/conf/server.xml<Host>标记内:

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

这样他们就可以通过http://example.com/files/...访问了。可以找到GlassFish / Payara配置示例here,可以找到WildFly配置示例here

如果你想自己控制读/写文件,那么你需要为此创建一个Servlet,它基本上只获得文件的InputStream,例如{{1}然后将其写入FileInputStream的{​​{1}}。

在响应中,您应设置OutputStream标头,以便客户端知道与提供的文件关联的应用程序。并且,您应该设置HttpServletResponse标头,以便客户端可以计算下载进度,否则将是未知的。并且,如果您需要另存为对话框,则应将Content-Type标头设置为Content-Length,否则客户端将尝试将其显示为内联。最后,只需将文件内容写入响应输出流。

以下是这种servlet的基本示例:

Content-Disposition

映射到例如attachment的{​​{1}}时,您可以通过@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()); } } 调用它。这样,您可以比url-pattern更多地控制请求,例如提供默认图像(即/files/*左右)。使用http://example.com/files/image.png也优先于DefaultServlet,因为它更符合SEO,否则IE将无法在另存为期间选择正确的文件名。

您可以重复使用相同的逻辑来从数据库中提供文件。只需将if (!file.exists()) file = new File("/path/to/files", "404.gif")替换为request.getPathInfo()

希望这有帮助。

另见:

答案 1 :(得分:9)

您可以将图像放在固定路径上(例如:/ var / images或c:\ images),在应用程序设置中添加一个设置(在我的示例中由Settings.class表示),并在你的HttpServlet中加载它们:

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

或者如果你想操纵图像:

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

然后html代码为<img src="imageServlet?imageName=myimage.png" />

当然你应该考虑提供不同的内容类型 - “image / jpeg”,例如基于文件扩展名。你也应该提供一些缓存。

此外,您可以使用此servlet对图像进行质量重新缩放,方法是提供宽度和高度参数作为参数,并使用image.getScaledInstance(w, h, Image.SCALE_SMOOTH),当然还要考虑性能。

答案 2 :(得分:6)

要求:从WEBROOT目录外部或从本地磁盘访问静态资源(图像/视频等)

步骤1:
 在tomcat服务器的webapps下创建一个文件夹,让我们说文件夹名是myproj

步骤2:
在myproj下创建一个WEB-INF文件夹,在此下创建一个简单的web.xml

web.xml下的

代码

<web-app>
</web-app>

以上两个步骤的目录结构

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

步骤3:
现在,在以下位置创建一个名为myproj.xml的xml文件

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

myproj.xml中的CODE:

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

步骤4:
 4 A)现在在硬盘的E盘中创建一个名为myproj的文件夹并创建一个新的

具有名称图像的文件夹,并将一些图像放在图像文件夹(e:myproj\images\)

让我们假设myfoto.jpg位于e:\myproj\images\myfoto.jpg

之下

4 B)现在在e:\myproj\WEB-INF中创建一个名为WEB-INF的文件夹,并在WEB-INF文件夹中创建一个web.xml

web.xml中的代码

<web-app>
</web-app>

步骤5:
现在创建一个名为index.html的.html文档,并放在e:\ myproj

index.html下的CODE                                                       欢迎来到Myproj                               

上述步骤4和步骤5的目录结构如下

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

步骤6:
现在启动apache tomcat服务器

步骤7:
打开浏览器并按如下所示键入URL

http://localhost:8080/myproj    

然后你显示index.html

中提供的内容

步骤8:
访问本地硬盘(webroot之外)下的图像

http://localhost:8080/myproj/images/myfoto.jpg

答案 3 :(得分:5)

添加到server.xml:

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

在web.xml中启用dir文件列表参数:

    <init-param>
        <param-name>listings</param-name>
        <param-value>true</param-value>
    </init-param>

答案 4 :(得分:5)

这是我工作场所的故事:
- 我们尝试使用Struts 1和Tomcat 7.x上传多个图像和文档文件 - 我们尝试将上传的文件写入文件系统,文件名和数据库记录的完整路径 - 我们尝试在网络应用程序目录之外分隔文件夹。 (*)

以下解决方案非常简单,对于要求(*)有效:

在文件 META-INF/context.xml 文件中包含以下内容: (例如,我的应用程序在http://localhost:8080/ABC运行,我的应用程序/项目名为ABC)。 (这也是文件context.xml

的完整内容
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/ABC" aliases="/images=D:\images,/docs=D:\docs"/>

(适用于Tomcat版本7或更高版本)

结果:我们创建了2个别名。例如,我们将图片保存在:D:\images\foo.jpg 并从链接或使用图像标记查看:

<img src="http://localhost:8080/ABC/images/foo.jsp" alt="Foo" height="142" width="142">

<img src="/images/foo.jsp" alt="Foo" height="142" width="142">

(我使用Netbeans 7.x,Netbeans似乎是自动创建文件WEB-INF\context.xml

答案 5 :(得分:2)

如果您决定发送至FileServlet,则allowLinking="true"中还需要context.xml才能允许FileServlet遍历符号链接。

请参阅http://tomcat.apache.org/tomcat-6.0-doc/config/context.html

答案 6 :(得分:0)

如果有人无法用已接受的答案解决他的问题,请注意以下注意事项:

  1. 无需提及具有localhost:<port>属性的<img> src
  2. 确保您在eclipse之外运行此项目,因为eclipse在其本地context docBase文件中创建了server.xml条目。

答案 7 :(得分:0)

读取文件的InputStream并将其写入ServletOutputStream,以便将二进制数据发送到客户端。

  • 本地文件您可以使用 FileInputStream('path / image.png')直接读取文件。
  • Mongo DataBase文件您可以 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));
    }
}

直接将结果显示在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>

答案 8 :(得分:0)

如果您想使用 JAX-RS (例如RESTEasy),请尝试以下操作:

@Path("/pic")
public Response get(@QueryParam("url") final String url) {
    String picUrl = URLDecoder.decode(url, "UTF-8");

    return Response.ok(sendPicAsStream(picUrl))
            .header(HttpHeaders.CONTENT_TYPE, "image/jpg")
            .build();
}

private StreamingOutput sendPicAsStream(String picUrl) {
    return output -> {
        try (InputStream is = (new URL(picUrl)).openStream()) {
            ByteStreams.copy(is, output);
        }
    };
}

使用javax.ws.rs.core.Responsecom.google.common.io.ByteStreams

答案 9 :(得分:-1)

我做得更简单。问题:CSS文件的url链接到img文件夹。获得404.

我查看了不存在的网址http://tomcatfolder:port/img/blablah.png。但是,这真的指向Tomcat中的ROOT应用程序。

所以我只是将我的webapp中的img文件夹复制到该ROOT应用程序中。作品!

当然不建议用于制作,但这适用于内部工具开发应用程序。