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

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

通过将静态数据存储在 web 容器中来实现这一点不是问题,但是从 web 容器外部存储和加载静态数据却让我头疼。

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

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

一些人建议编写一个过滤器或 servlet 来处理所提供的图像,但是这些建议非常模糊和高级,没有指出如何实现这一点的更详细信息。

165467 次浏览

你可以把你的图片放在一个固定的路径上(例如:/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”,例如基于文件扩展名。您还应该提供一些缓存。

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

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

如果你遵循 * nix 文件系统路径规则(比如像在 /path/to/files中一样只使用正斜杠) ,那么它在 Windows 上也能正常工作,不需要摆弄难看的 File.separator字符串连接。但是,只能在调用该命令的同一个工作磁盘上扫描它。例如,如果 Tomcat 安装在 C:上,那么 /path/to/files实际上指向 C:\path\to\files

如果所有的文件都位于 webapp 之外,并且您想要 Tomcat 的 DefaultServlet来处理它们,那么您在 Tomcat 中基本上需要做的就是在 <Host>标记中向 /conf/server.xml添加以下 Context 元素:

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

这样就可以通过 http://example.com/files/...访问它们了。对于基于 Tomcat 的服务器,如 JBoss EAP 6.x 或更老版本,其方法基本相同,也请参见 给你。GlassFish/Payara 配置示例可以找到 给你,WildFly 配置示例可以找到 给你

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

在响应上,您应该设置 Content-Type头,以便客户端知道哪个应用程序与提供的文件相关联。并且,您应该设置 Content-Length头,以便客户端可以计算下载进度,否则它将是未知的。而且,如果需要 除了 A对话框,应该将 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());
}


}

当映射到例如 /files/*url-pattern时,可以通过 http://example.com/files/image.png调用它。通过这种方式,您可以比 DefaultServlet对请求有更多的控制,比如提供一个默认映像(即 if (!file.exists()) file = new File("/path/to/files", "404.gif")左右)。也使用 request.getPathInfo()是首选以上的 request.getParameter(),因为它是更搜索引擎优化友好,否则 IE 将不会选择正确的文件名在 除了 A

您可以重用相同的逻辑来为数据库中的文件提供服务。

希望这个能帮上忙。

参见:

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

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

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

第一步:
在 tomcat server.webapps 下创建一个文件夹,假设文件夹名为 myproj

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

Xml 下的代码

<web-app>
</web-app>

上面两个步骤的目录结构

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

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

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

Xml 中的代码:

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

第四步:
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

Xml 中的代码

<web-app>
</web-app>

步骤5: < br > 现在创建一个名为 index.html 的. html 文档,放在 e: myproj 下面

Index.html 下的代码 欢迎来到 Myproj

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

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

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

步骤7: < br > 打开浏览器,键入如下 URL

http://localhost:8080/myproj

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

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

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

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

这是我工作的地方发生的故事:
- 我们尝试使用 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)

问题: 一个 CSS 文件有指向 img 文件夹的 URL 链接。

我看了一下 url,http://tomcatfolder:port/img/blablah.png,它并不存在,但是,它实际上指向 Tomcat 中的 ROOT 应用程序。

因此,我只是将 img 文件夹从我的 webapp 复制到了那个 ROOT 应用程序中!

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

如果有人不能用公认的答案解决他的问题,请注意以下几点:

  1. 不需要提到 localhost:<port><img> src属性。
  2. 请确保您正在 Eclipse 之外运行该项目,因为 eclipse 在其本地 server.xml文件内独立创建 context docBase条目。

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

  • 本地文件 可以使用 FileInputStream (‘ path/image. png’)直接读取文件。
  • Mongo 数据库文件的 可以 使用 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>

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