Java 用于提供静态内容的 Servlet

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/132052/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 08:39:37  来源:igfitidea点击:

Servlet for serving static content

javajspservletsjakarta-ee

提问by Bruno De Fraine

I deploy a webapp on two different containers (Tomcat and Jetty), but their default servlets for serving the static content have a different way of handling the URL structure I want to use (details).

我在两个不同的容器(Tomcat 和 Jetty)上部署了一个 web 应用程序,但是它们用于提供静态内容的默认 servlet 具有不同的处理我想要使用的 URL 结构的方式(详细信息)。

I am therefore looking to include a small servlet in the webapp to serve its own static content (images, CSS, etc.). The servlet should have the following properties:

因此,我希望在 webapp 中包含一个小的 servlet 来提供它自己的静态内容(图像、CSS 等)。servlet 应具有以下属性:

  • No external dependencies
  • Simple and reliable
  • Support for If-Modified-Sinceheader (i.e. custom getLastModifiedmethod)
  • (Optional) support for gzip encoding, etags,...

Is such a servlet available somewhere? The closest I can find is example 4-10from the servlet book.

这样的 servlet 在某处可用吗?我能找到的最接近的是servlet 书中的示例 4-10

Update:The URL structure I want to use - in case you are wondering - is simply:

更新:我想使用的 URL 结构 - 如果你想知道 - 很简单:

    <servlet-mapping>
            <servlet-name>main</servlet-name>
            <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>/static/*</url-pattern>
    </servlet-mapping>

So all requests should be passed to the main servlet, unless they are for the staticpath. The problem is that Tomcat's default servlet does not take the ServletPath into account (so it looks for the static files in the main folder), while Jetty does (so it looks in the staticfolder).

所以所有请求都应该传递给主 servlet,除非它们是针对static路径的。问题是 Tomcat 的默认 servlet 没有考虑 ServletPath(因此它会在主文件夹中查找静态文件),而 Jetty 会(因此它会在static文件夹中查找)。

采纳答案by Bruno De Fraine

I ended up rolling my own StaticServlet. It supports If-Modified-Since, gzip encoding and it should be able to serve static files from war-files as well. It is not very difficult code, but it is not entirely trivial either.

我最终滚动了自己的StaticServlet. 它支持If-Modified-Since, gzip 编码,它也应该能够从战争文件中提供静态文件。这不是很困难的代码,但也不是完全微不足道的。

The code is available: StaticServlet.java. Feel free to comment.

代码可用:StaticServlet.java。随意发表评论。

Update:Khurram asks about the ServletUtilsclass which is referenced in StaticServlet. It is simply a class with auxiliary methods that I used for my project. The only method you need is coalesce(which is identical to the SQL function COALESCE). This is the code:

更新:Khurram问及ServletUtils这是在引用的类StaticServlet。它只是一个带有辅助方法的类,用于我的项目。您需要的唯一方法是coalesce(与 SQL 函数相同COALESCE)。这是代码:

public static <T> T coalesce(T...ts) {
    for(T t: ts)
        if(t != null)
            return t;
    return null;
}

回答by Panagiotis Korros

I had the same problem and I solved it by using the code of the 'default servlet' from the Tomcat codebase.

我遇到了同样的问题,我使用 Tomcat 代码库中的“默认 servlet”代码解决了这个问题。

http://svn.apache.org/repos/asf/tomcat/trunk/java/org/apache/catalina/servlets/DefaultServlet.java

http://svn.apache.org/repos/asf/tomcat/trunk/java/org/apache/catalina/servlets/DefaultServlet.java

The DefaultServletis the servlet that serves the static resources (jpg,html,css,gif etc) in Tomcat.

DefaultServlet是在Tomcat中提供的静态资源(JPG,HTML,CSS,GIF等),该servlet。

This servlet is very efficient and has some the properties you defined above.

这个 servlet 非常高效,并且具有您在上面定义的一些属性。

I think that this source code, is a good way to start and remove the functionality or depedencies you don't need.

我认为此源代码是启动和删除不需要的功能或依赖项的好方法。

  • References to the org.apache.naming.resources package can be removed or replaced with java.io.File code.
  • References to the org.apache.catalina.util package are propably only utility methods/classes that can be duplicated in your source code.
  • References to the org.apache.catalina.Globals class can be inlined or removed.
  • 对 org.apache.naming.resources 包的引用可以删除或替换为 java.io.File 代码。
  • 对 org.apache.catalina.util 包的引用可能只是可以在源代码中复制的实用程序方法/类。
  • 可以内联或删除对 org.apache.catalina.Globals 类的引用。

回答by yogman

Use org.mortbay.jetty.handler.ContextHandler. You don't need additional components like StaticServlet.

使用 org.mortbay.jetty.handler.ContextHandler。您不需要像 StaticServlet 这样的附加组件。

At the jetty home,

在码头之家,

$ cd contexts

$ cd 上下文

$ cp javadoc.xml static.xml

$ cp javadoc.xml static.xml

$ vi static.xml

$ vi static.xml

...

...

<Configure class="org.mortbay.jetty.handler.ContextHandler">
<Set name="contextPath">/static</Set>
<Set name="resourceBase"><SystemProperty name="jetty.home" default="."/>/static/</Set>
<Set name="handler">
  <New class="org.mortbay.jetty.handler.ResourceHandler">
    <Set name="cacheControl">max-age=3600,public</Set>
  </New>
 </Set>
</Configure>

Set the value of contextPath with your URL prefix, and set the value of resourceBase as the file path of the static content.

将 contextPath 的值设置为您的 URL 前缀,并将 resourceBase 的值设置为静态内容的文件路径。

It worked for me.

它对我有用。

回答by axtavt

There is no need for completely custom implementation of the default servlet in this case, you can use this simple servlet to wrap request to the container's implementation:

在这种情况下,不需要完全自定义默认 servlet 的实现,您可以使用这个简单的 servlet 将请求包装到容器的实现中:


package com.example;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class DefaultWrapperServlet extends HttpServlet
{   
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        RequestDispatcher rd = getServletContext().getNamedDispatcher("default");

        HttpServletRequest wrapped = new HttpServletRequestWrapper(req) {
            public String getServletPath() { return ""; }
        };

        rd.forward(wrapped, resp);
    }
}

回答by Coldbeans Software

回答by Coldbeans Software

I found great tutorial on the web about some workaround. It is simple and efficient, I used it in several projects with REST urls styles approach:

我在网上找到了关于一些解决方法的很棒的教程。它简单高效,我在几个项目中使用了 REST urls 样式方法:

http://www.kuligowski.pl/java/rest-style-urls-and-url-mapping-for-static-content-apache-tomcat,5

http://www.kuligowski.pl/java/rest-style-urls-and-url-mapping-for-static-content-apache-tomcat,5

回答by delux247

I did this by extending the tomcat DefaultServlet(src) and overriding the getRelativePath() method.

我通过扩展 tomcat DefaultServlet( src) 并覆盖 getRelativePath() 方法来做到这一点。

package com.example;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.servlets.DefaultServlet;

public class StaticServlet extends DefaultServlet
{
   protected String pathPrefix = "/static";

   public void init(ServletConfig config) throws ServletException
   {
      super.init(config);

      if (config.getInitParameter("pathPrefix") != null)
      {
         pathPrefix = config.getInitParameter("pathPrefix");
      }
   }

   protected String getRelativePath(HttpServletRequest req)
   {
      return pathPrefix + super.getRelativePath(req);
   }
}

... And here are my servlet mappings

...这是我的 servlet 映射

<servlet>
    <servlet-name>StaticServlet</servlet-name>
    <servlet-class>com.example.StaticServlet</servlet-class>
    <init-param>
        <param-name>pathPrefix</param-name>
        <param-value>/static</param-value>
    </init-param>       
</servlet>

<servlet-mapping>
    <servlet-name>StaticServlet</servlet-name>
    <url-pattern>/static/*</url-pattern>
</servlet-mapping>  

回答by Will Hartung

I've had good results with FileServlet, as it supports pretty much all of HTTP (etags, chunking, etc.).

我使用FileServlet取得了不错的结果,因为它几乎支持所有 HTTP(etags、分块等)。

回答by Will Hartung

To serve all requests from a Spring app as well as /favicon.ico and the JSP files from /WEB-INF/jsp/* that Spring's AbstractUrlBasedView will request you can just remap the jsp servlet and default servlet:

要处理来自 Spring 应用程序的所有请求以及 /favicon.ico 和来自 Spring 的 AbstractUrlBasedView 将请求的 /WEB-INF/jsp/* 的 JSP 文件,您只需重新映射 jsp servlet 和默认 servlet:

  <servlet>
    <servlet-name>springapp</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>/WEB-INF/jsp/*</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/favicon.ico</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>springapp</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

We can't rely on the *.jsp url-pattern on the standard mapping for the jsp servlet because the path pattern '/*' is matched before any extension mapping is checked. Mapping the jsp servlet to a deeper folder means it's matched first. Matching '/favicon.ico' exactly happens before path pattern matching. Deeper path matches will work, or exact matches, but no extension matches can make it past the '/*' path match. Mapping '/' to default servlet doesn't appear to work. You'd think the exact '/' would beat the '/*' path pattern on springapp.

我们不能依赖于 jsp servlet 标准映射上的 *.jsp url-pattern,因为在检查任何扩展映射之前,路径模式“/*”已匹配。将 jsp servlet 映射到更深的文件夹意味着它首先匹配。匹配 '/favicon.ico' 恰好发生在路径模式匹配之前。更深的路径匹配将起作用,或者完全匹配,但没有扩展匹配可以使其超过“/*”路径匹配。将“/”映射到默认 servlet 似乎不起作用。您会认为确切的 '/' 会击败 springapp 上的 '/*' 路径模式。

The above filter solution doesn't work for forwarded/included JSP requests from the application. To make it work I had to apply the filter to springapp directly, at which point the url-pattern matching was useless as all requests that go to the application also go to its filters. So I added pattern matching to the filter and then learned about the 'jsp' servlet and saw that it doesn't remove the path prefix like the default servlet does. That solved my problem, which was not exactly the same but common enough.

上述过滤器解决方案不适用于来自应用程序的转发/包含的 JSP 请求。为了使其工作,我必须直接将过滤器应用于 springapp,此时 url-pattern 匹配是无用的,因为所有进入应用程序的请求也进入其过滤器。因此,我向过滤器添加了模式匹配,然后了解了 'jsp' servlet,发现它不像默认 servlet 那样删除路径前缀。这解决了我的问题,这并不完全相同但足够普遍。

回答by Taylor Gautier

I came up with a slightly different solution. It's a bit hack-ish, but here is the mapping:

我想出了一个稍微不同的解决方案。这有点hack-ish,但这是映射:

<servlet-mapping>   
    <servlet-name>default</servlet-name>
    <url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
 <servlet-name>default</servlet-name>
    <url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>myAppServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

This basically just maps all content files by extension to the default servlet, and everything else to "myAppServlet".

这基本上只是将所有内容文件按扩展名映射到默认 servlet,其他所有内容都映射到“myAppServlet”。

It works in both Jetty and Tomcat.

它适用于 Jetty 和 Tomcat。