解决SpringBoot无法读取js/css静态资源的新方法
前言
作为依赖使用的SpringBoot工程很容易出现自身静态资源被主工程忽略的情况。但是作为依赖而存在的Controller方法却不会失效,我们知道,Spring MVC对于静态资源的处理也不外乎是路径匹配,读取资源封装到Response中响应给浏览器,所以,解决的途径就是自己写一个读取Classpath下静态文件并响应给客户端的方法。
对于ClassPath下文件的读取,最容易出现的就是IDE运行ok,打成jar包就无法访问了,该问题的原因还是在于getResources()不如getResourceAsStream()方法靠谱。
读取classpath文件
本就是SpringBoot的问题场景,何不用Spring现成的ClassPathResource类呢?
ReadClasspathFile.java
public class ReadClasspathFile {
public static String read(String classPath) throws IOException {
ClassPathResource resource = new ClassPathResource(classPath);
BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream(),"UTF-8"));
StringBuilder builder = new StringBuilder();
String line;
while ((line = reader.readLine())!=null){
builder.append(line+"\n");
}
return builder.toString();
}
}
上面的代码并不是特别规范,存在多处漏洞。比如没有关闭IO流,没有判断文件是否存在,没有考虑到使用缓存进行优化。
这里为什么考虑缓存呢?如果不加缓存,那么每次请求都涉及IO操作,开销也比较大。关于缓存的设计,这里使用WeakHashMap,最终代码如下:
public class ReadClasspathFile {
private static WeakHashMap<String, String> map = new WeakHashMap<>();
public static String read(String classPath) {
//考虑到数据的一致性,这里没有使用map的containsKey()
String s = map.get(classPath);
if (s != null) {
return s;
}
//判空
ClassPathResource resource = new ClassPathResource(classPath);
if (!resource.exists()) {
return null;
}
//读取
StringBuilder builder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream(), "UTF-8"))) {
String line;
while ((line = reader.readLine()) != null) {
builder.append(line).append("\n");
}
} catch (IOException e) {
e.printStackTrace();
}
//DCL双检查锁
if (!map.containsKey(classPath)) {
synchronized (ReadClasspathFile.class) {
if (!map.containsKey(classPath)) {
map.put(classPath, builder.toString());
}
}
}
return builder.toString();
}
}
但这样就完美了吗?其实不然。对于html/css等文本文件,这样看起来似乎并没有什么错误,但对于一些二进制文件,就会导致浏览器解码出错。为了万无一失,服务端应该完全做到向客户端返回原生二进制流,也就是字节数组。具体的解码应由浏览器进行判断并实行。
public class ReadClasspathFile {
private static WeakHashMap<String, byte[]> map = new WeakHashMap<>();
public static byte[] read(String classPath) {
//考虑到数据的一致性,这里没有使用map的containsKey()
byte[] s = map.get(classPath);
if (s != null) {
return s;
}
//判空
ClassPathResource resource = new ClassPathResource(classPath);
if (!resource.exists()) {
return null;
}
//读取
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(resource.getInputStream());
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(stream)) {
byte[] bytes = new byte[1024];
int n;
while ((n = bufferedInputStream.read(bytes))!=-1){
bufferedOutputStream.write(bytes,0,n);
}
} catch (IOException e) {
e.printStackTrace();
}
//DCL双检查锁
if (!map.containsKey(classPath)) {
synchronized (ReadClasspathFile.class) {
if (!map.containsKey(classPath)) {
map.put(classPath, stream.toByteArray());
}
}
}
return stream.toByteArray();
}
}
自定义映射
接下来就是Controller层进行映射匹配响应了,这里利用Spring MVC取个巧,代码如下:
@ResponseBody
@RequestMapping(value = "view/{path}.html",produces = {"text/html; charset=UTF-8"})
public String view_html(@PathVariable String path) throws IOException {
return ReadClasspathFile.read("view/"+path+".html");
}
@ResponseBody
@RequestMapping(value = "view/{path}.js",produces = {"application/x-javascript; charset=UTF-8"})
public String view_js(@PathVariable String path) throws IOException {
return ReadClasspathFile.read("view/"+path+".js");
}
@ResponseBody
@RequestMapping(value = "view/{path}.css",produces = {"text/css; charset=UTF-8"})
public String view_html(@PathVariable String path) throws IOException {
return ReadClasspathFile.read("view/"+path+".css");
}
通过后戳(html、js)进行判断,以应对不同的Content-Type类型,静态资源的位置也显而易见,位于resources/view下。
但是,使用@PathVariable注解的这种方式不支持多级路径,也就是不支持包含“/”,为了支持匹配多级目录,我们只能放弃这种方案,使用另一种方案。
@ResponseBody
@RequestMapping(value = "/view/**",method = RequestMethod.GET)
public void view_js(HttpServletResponse response, HttpServletRequest request) throws IOException {
String uri = request.getRequestURI().trim();
if (uri.endsWith(".js")){
response.setContentType("application/javascript");
}else if (uri.endsWith(".css")){
response.setContentType("text/css");
}else if (uri.endsWith(".ttf")||uri.endsWith(".woff")){
response.setContentType("application/octet-stream");
}else {
String contentType = new MimetypesFileTypeMap().getContentType(uri);
response.setContentType(contentType);
}
response.getWriter().print(ReadClasspathFile.read(uri));
}
将读取文件的静态方法更换为我们最新的返回字节流的方法,最终代码为:
@RequestMapping(value = "/tree/**",method = RequestMethod.GET)
public void view_js(HttpServletResponse response, HttpServletRequest request) throws IOException {
String uri = request.getRequestURI().trim();
if (uri.endsWith(".js")){
response.setContentType("application/javascript");
}else if (uri.endsWith(".css")){
response.setContentType("text/css");
}else if (uri.endsWith(".woff")){
response.setContentType("application/x-font-woff");
}else if (uri.endsWith(".ttf")){
response.setContentType("application/x-font-truetype");
}else if (uri.endsWith(".html")){
response.setContentType("text/html");
}
byte[] s = ReadClasspathFile.read(uri);
response.getOutputStream().write(Optional.ofNullable(s).orElse("404".getBytes()));
}
解决SpringBoot无法读取js/css静态资源的新方法的更多相关文章
- 使用Maven + Jetty时,如何不锁定js css 静态资源
Jetty会使用内存映射文件来缓存静态文件,包括js,css文件. 在Windows下,使用内存映射文件会导致文件被锁定,所以当Jetty启动的时候无法在编辑器对js或者css文件进行编辑. 解决办法 ...
- springboot 项目中css js 等静态资源无法访问的问题
目录 问题场景 问题分析 问题解决 问题场景 今天在开发一个springboot 项目的时候突然发现 css js 等静态资源竟然都报404找不到,折腾了好久终于把问题都解决了,决定写篇博客,纪录总结 ...
- 十二、springboot之web开发之静态资源处理
springboot静态资源处理 Spring Boot 默认为我们提供了静态资源处理,使用 WebMvcAutoConfiguration 中的配置各种属性. 建议大家使用Spring Boot的默 ...
- SpringBoot第四集:静态资源与首页定(2020最新最易懂)
SpringBoot第四集:静态资源与首页定(2020最新最易懂) 问题 SpringBoot构建的项目结构如下:没有webapp目录,没有WEB-INF等目录,那么如果开发web项目,项目资源放在那 ...
- 使用Node.js搭建静态资源服务器
对于Node.js新手,搭建一个静态资源服务器是个不错的锻炼,从最简单的返回文件或错误开始,渐进增强,还可以逐步加深对http的理解.那就开始吧,让我们的双手沾满网络请求! Note: 当然在项目中如 ...
- [Asp.net Mvc]为js,css静态文件添加版本号
方式一: 思路 string version = ViewBag.Version; @Scripts.RenderFormat("<script type=\"text/ja ...
- springboot 集成swagger2.x 后静态资源报404
package com.bgs360.configuration; import org.springframework.context.EnvironmentAware; import org.sp ...
- springboot拦截器拦了静态资源css,js,png,jpeg,svg等等静态资源
1.在SpringBoot中自己写的拦截器,居然把静态资源也拦截了,导致了页面加载失败. package com.bie.config; import com.bie.component.MyLoca ...
- 解决Tomcat无法加载css和js等静态资源文件
解决思路有两个 一是,你使用了Apache服务器,html不交给Tomcat处理,所以你找不到Html等静态资源,所以你先停掉阿帕奇,然后只用Tomcat猫试试. 二是,像我一样,使用了Jetty开发 ...
随机推荐
- 智能家居-3.基于esp8266的语音控制系统(软件篇)
智能家居-1.基于esp8266的语音控制系统(开篇) 智能家居-2.基于esp8266的语音控制系统(硬件篇) 智能家居-3.基于esp8266的语音控制系统(软件篇) 赞赏支持 QQ:505645 ...
- WebService 创建、发布、调用
环境Win7+VS2017 启用IIS 查看iis是否启用 新建 ASP.NET Web 应用程序 项目,项目中添加Web 服务 在 asmx 文件中添加需要的方法 运行结果 发布 创建新的文件夹, ...
- Android四大组件:BroadcastReceiver 介绍
介绍 BroadcastReceiver 即广播组件,是 Android 的四大组件之一.用于监听和接收广播消息,并做出响应.有以下一些应用: 不同组件之间的通信(应用内或不同应用之间). 多线程之间 ...
- Hibernate与Mybatis 对比
见知乎:https://www.zhihu.com/question/21104468 总结: 1:业务简单,不涉及多表关联查询的,用Hibernate更快,但是当业务量上去后,需要精通Hiberna ...
- odoo10学习笔记二:继承(扩展)、模块数据
转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/11189252.html 一:继承 在不改变底层对象的时候添加新的功能——这是通过继承机制来实现的,作为在现有 ...
- Rust中的字符串处理
一路看过来,怕是我知道的所有语言当,处理最复杂吧. 当然,如果能正确处理,也是能理解最到位的. 这,就是我为什么要学Rust的原因. 暂无用武之地,但逻辑体系和知识点够复杂,才能应对更多事务~ fn ...
- 201971010110-李华《面向对象程序设计(java)》第七周学习总结
博文正文开头格式:(2分) 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.co ...
- UiPath:Split(","c)以逗号区分遍历字符串数组
学习中遇到同一用户多种职业的情况,因为所有的数据都是从Excel里面取的,所以只能把所有的职业写在一个单元格里,以逗号区分. 那么就需要先把字符串转为数组,然后遍历数组去添加职位.如图 Split(& ...
- Mybatis-Plus Bugs
Mybatis-Plus Bugs 实体类中属性和数据库字段对应异常 Caused by: java.sql.SQLSyntaxErrorException: Unknown column 'user ...
- shell的变量以及常见符号
shell的变量以及常见符号 常见变量 不同于其它语言需要先声明变量,shell的变量直接使用 eg: a=15 调用变量的话 $a 或者 ${a} $? #判断上一条命令执行的是否成功 0 ...