136

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

通过将静态数据存储在 Web 容器中来做到这一点不是问题,但是从 Web 容器外部存储和加载它们让我很头疼。

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

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

一些人建议编写一个过滤器或一个 servlet 来处理图像服务,但这些建议非常模糊和高级,没有指向有关如何完成此操作的更详细信息的指针。

4

10 回答 10

172

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

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

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

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

这样他们就可以通过http://example.com/files/.... 对于基于 Tomcat 的服务器,例如 JBoss EAP 6.x 或更早版本,方法基本相同,另请参见此处。GlassFish/Payara 配置示例可以在这里找到,WildFly 配置示例可以在这里找到。

如果您想自己控制读/写文件,那么您需要为此创建一个Servlet,它基本上只是获取文件中的一个,InputStream例如FileInputStream并将其写入OutputStream.HttpServletResponse

在响应中,您应该设置Content-Type标头,以便客户端知道哪个应用程序与提供的文件相关联。并且,你应该设置Content-Lengthheader,以便客户端可以计算下载进度,否则将是未知的。并且,如果您想要“另存为”对话框,则应将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例如,当映射到of 时/files/*,您可以调用它http://example.com/files/image.png。通过这种方式,您可以对请求进行比实际更多的控制DefaultServlet,例如提供默认图像(即if (!file.exists()) file = new File("/path/to/files", "404.gif")左右)。request.getPathInfo()上面也首选使用,因为它对 SEO 更友好,否则 IE 在Save Asrequest.getParameter()期间不会选择正确的文件名。

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

希望这可以帮助。

也可以看看:

于 2009-11-28T11:57:53.137 回答
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),当然要考虑性能。

于 2009-11-28T11:30:14.433 回答
8

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

第一步:
在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 下的代码 欢迎使用 Myproj

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

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

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

第七步:
打开浏览器,输入如下网址

http://localhost:8080/myproj    

然后你显示在 index.html 中提供的内容

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

http://localhost:8080/myproj/images/myfoto.jpg
于 2013-03-16T04:46:25.003 回答
8

添加到 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>
于 2015-05-12T10:27:58.287 回答
6

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

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

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

于 2015-06-02T16:01:33.913 回答
2

如果您决定发送到,FileServlet那么您还需要allowLinking="true"incontext.xml以允许FileServlet遍历符号链接。

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

于 2011-01-25T17:03:01.620 回答
1

如果你想使用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

于 2018-04-03T01:04:21.713 回答
0

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

  1. 无需提及属性localhost:<port><img> src
  2. 确保您在 Eclipse 之外运行此项目,因为 Eclipse 会context docBase在其本地server.xml文件中自行创建条目。
于 2017-10-26T08:37:03.560 回答
0

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

@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>
于 2018-01-12T11:13:45.090 回答
-1

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

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

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

当然,不推荐用于生产,但这是用于内部工具开发应用程序。

于 2016-08-31T14:57:02.827 回答