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


131

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

通过将静态数据存储在Web容器中来执行此操作不是问题,但是从Web容器外部存储和加载静态数据让我头疼。

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

我已经看到了一些建议,例如将图像目录作为指向Web容器外部目录的符号链接,但是这种方法在Windows和* nix环境下都可以使用吗?

有些人建议编写一个用于处理图像服务的过滤器或Servlet,但这些建议非常模糊且笼统,没有指向如何实现此目的的更详细信息的指针。

Answers:


161

我已经看到了一些建议,例如将图像目录作为指向Web容器外部目录的符号链接,但是这种方法在Windows和* nix环境下都可以使用吗?

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

如果这些文件都位于webapp外部,并且您想让Tomcat DefaultServlet处理这些文件,则在Tomcat中基本要做的就是将以下Context元素添加到/conf/server.xmlinside <Host>标记中:

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

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

如果您想自己控制文件的读写,则需要为此创建一个Servlet,它基本上只是InputStream以例如的形式获取文件的FileInputStream并将其写入的OutputStreamHttpServletResponse

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

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

}

url-pattern例如/files/*,当映射到的时,您可以通过调用它http://example.com/files/image.png。这样,您可以对请求进行更多控制DefaultServlet,例如提供默认图像(即if (!file.exists()) file = new File("/path/to/files", "404.gif")左右)。另外,request.getPathInfo()最好使用上面的,request.getParameter()因为它对SEO更加友好,否则IE在“ 另存为”期间将不会选择正确的文件名。

您可以重复使用相同的逻辑从数据库提供文件。只需替换new FileInputStream()ResultSet#getInputStream()

希望这可以帮助。

也可以看看:


1
@SalutonMondo:省力的方式。
BalusC 2015年

@BalusC,我尝试了此操作:<Context docBase="/path/to/images" path="/images" />在Windows中,但获取相对于webapps文件夹的路径:C:\install\apache-tomcat-8.0.26\webapps\tmp] is not valid
ACV

在Windows上应该是:<Context docBase="C:\tmp\" path="/images" />
ACV

HTTP Status 404 - /images/在执行以下操作时:http://localhost:8080/images/,但从tmp工作中获取特定文件:http://localhost:8080/images/Tulips.jpg是可以的
ACV 2015年

这样,如果我将图像放在文件夹中,除非重新启动服务器,否则图像的链接将给出响应404,请问有什么原因吗?
Mateus Viccari '16

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”,例如基于文件扩展名。另外,您应该提供一些缓存。

另外,您可以通过提供width和height参数作为参数并使用image.getScaledInstance(w, h, Image.SCALE_SMOOTH)(当然还要考虑到性能)来将该servlet用于图像的质量缩放。


2
您确实不需要Java 2D API,它只会不必要地增加更多开销。只需读取InputStream并写入OutputStream。
BalusC,2009年

1
是的,我以重新缩放和其他操作的想法开始了响应,但最终简化了它。
09年

7

添加到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>

通过更改web.xml,我可以获取文件列表,以便将其发送到选择框?
Tomasz Waszczyk '16

1
此更改将在tomcat的web.xml中,而不是您的应用程序中
vsingh

谢谢你!是否需要在web.xml中启用dir文件列表参数?
dariru

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中的代码:

<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)现在在其中创建一个名称为WEB-INF的文件夹,e:\myproj\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

您能否建议我,如何为动态值做同样的事情。我的意思是我想将data(xml)写入我的本地目录或中,并在我的jsp页面中读取它。有什么方法可以写入托管到目录的服务器,以便我可以使用上述过程访问它们?
2014年

尽管我可以正常运行index.html文件,但Web浏览器中没有图像显示
rogerwar 2015年

我的错帖工作正常,我忘了放/在E:/ myproj末尾,我将其更改为E:/ myproj /及其工作正常,谢谢@sbabamca
rogerwar 2015年

嗨,感谢您的帖子,这非常有用。在这里,我希望通过接口将文件上传到该特定目录。我希望同样启用POST方法。有人可以帮我吗?
vicky 2015年

6

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

以下解决方案非常简单,可以有效满足要求(*):

META-INF/context.xml具有以下内容的文件文件中:(例如,我的应用程序运行在http://localhost:8080/ABC,我的应用程序/项目名为ABC)。(这也是file的完整内容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



0

如果任何人都无法通过接受的答案解决他的问题,请注意以下注意事项:

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

0

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

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

结果直接将URL发送到src服装。

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

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


-1

我做得更简单。问题:CSS文件具有指向img文件夹的URL链接。获取404。

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

因此,我只是将img文件夹从我的Web应用程序复制到了该ROOT应用程序中。作品!

当然不建议用于生产环境,但这是针对内部工具开发应用程序的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.