我以前写过一个使用DefaultServlet
实现下载的博客springboot 添加defaultServlet实现更好的文件下载功能 ,这篇文章是对这类问题的多种解法讲解。
目的
将图片存储在f:\\test
文件夹或其子文件夹下,实现访问http://localhost/static/1.jpg
网页上展示图片,访问http://localhost/download/1.jpg
浏览器下载图片。
方法1
请参考这篇文章的实现, springboot 添加defaultServlet实现更好的文件下载功能,原理是配置一个DefaultServlet
来处理所有静态资源请求。
方法2
原理使用Controller
拦截掉静态资源的请求,转发到DefaultServlet
。
配置Controller如下
,使用RequestMapping
分开配置,处理下载的/download/**
和处理展示的 /static/**
分开。
他们俩的唯一区别是,/download/**
手动设置了响应头,在此设置的content-type
,后面就不会被设置了,否则会根据资源类型自适应content-type
。
调用ServletContext
的Dispatcher
做转发。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@RestController
public class ResourceController implements ServletContextAware {
//拦截资源,设置响应头application/octet-stream,浏览器表现为下载
@RequestMapping("/download/**")
public void download(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/octet-stream");
RequestDispatcher rd = req.getServletContext().getNamedDispatcher("default");
rd.forward(req, resp);
}
//拦截static/**,不覆盖响应头,根据类型自适应响应头
@RequestMapping("/static/**")
public void staticSource(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher rd = req.getServletContext().getNamedDispatcher("default");
rd.forward(req, resp);
}
//继承ServletContextAware,此方法内配置
//文件根目录,需要删除的虚拟路径
@Override
public void setServletContext(ServletContext servletContext) {
WebResourceRoot attribute = (WebResourceRoot) servletContext.getAttribute(Globals.RESOURCES_ATTR);
String basePath = "F:\\test";
//假设请求连接是 http://localhost/static/abc/123.jpg
//则实际查找路径是 F:\\test/abc/123.jpg
//相当于拿path 减去 下面的第二个参数,再拼接在basePath后面
attribute.addPreResources(new DirResourceSet(attribute, "/static", basePath, "/"));
attribute.addPreResources(new DirResourceSet(attribute, "/download", basePath, "/"));
}
}
上面的做法是在Controller
里面拦截请求,再请求转发到DefaultServlet
上,这样就不用配置DefaultServlet
的ServletMapper
了,使用SpringMvc
来管理路径问题。
这种方法默认defaultServlet
被配置(Tomcat
环境下是会被配置的)。
方法3
本方法使用Spring
的实现,即使底层用的不是Tomcat
,Spring
也能将这种差异屏蔽。
SpringBoot
默认会按顺序扫描以下目录的静态资源文件,这是SpringBoot
默认配置的,默认拦截路径是/**
。
1
2
3
4
5
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/",
"/"
写一个类继承WebMvcConfigurationSupport
,实现addResourceHandlers
方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//文件写法
registry.addResourceHandler("/static/**", "/download/**")
.addResourceLocations("file:F:/test/");
//classpath 写法
registry.addResourceHandler("/classpath/**")
.addResourceLocations("classpath:/static/", "classpath:/META-INF/resources/");
}
}
要注意的点:
-
资源在外部文件夹下的路径写法要以
file:
开头,以/
结尾,如file:D:/test/
-
访问
classpath
的资源,路径要以classpath:/
开头,以/
结尾,如classpath:/static/
-
访问
第三方jar
内部的静态文件,配置是classpath:/META-INF/resources/
-
像下面这样配置两个相同的路径,则后面覆盖前面的。
-
1 2
registry.addResourceHandler("/classpath/**").addResourceLocations("file:D:/test") registry.addResourceHandler("/classpath/**").addResourceLocations("file:E:/zz")
-
-
切记不要配置成
classpath:/
,这会导致resources
文件夹暴露出来,导致配置文件泄露
但是这种方法并不能实现文件下载,能识别出content-type
的如图片会自动打开在浏览器上,不能识别的资源才会调用浏览器下载。
关于自动配置
-
自动配置
一旦继承了
WebMvcConfigurationSupport
类,即使不重写addResourceHandlers
类,自动配置的5个路径就失效了。所以就不能访问静态文件了。
-
swagger-ui
的静态资源 在使用
swagger-ui
时,它的原理是将swagger-ui.html
页面打包到springfox.swagger-ui
里面了。要想访问这个页面需要配置下面的路径来处理。
1
registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/");
-
方法3搭配方法2
举个例子:
1. 将
/static
路径进行映射。1
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
2. 将参数中的文件名重新
forward
,这样也能实现根据参数提供静态文件名。1 2 3 4
@GetMapping("file") public void test(HttpServletRequest res, HttpServletResponse resp, String fileName) throws ServletException, IOException { res.getServletContext().getRequestDispatcher("/static/"+fileName).forward(res, resp); }