访问项目的静态资源是否可以正常的加载。所有的静态资源复制static目录下。
注意: idea对于JS代码的兼容性较差,编写了js代码但是有的时候不能够正常去加载。
1.idea缓存清理
2.clear-install
3.rebuild重新构建
4.重启idea和操作系统
拦截器
拦截器:首先将所有的请求统一拦截到拦截器中,可以拦截器中来定义过滤的规则,如果不满足系统的设置的过滤规则,统一的处理是重新去打开login.html页面(重定向和转发),推荐使用重定向。
在SpringBoot项目中拦截器的定义和使用。SpringBoot是依靠springMVC来完成的。SpringMVC提供了一个HandlerInterceptor接口,用于表示定义一个拦截器。受限制自定义个类,在这个类实现这个接口。
1.首先自定义一个类,在这个类实现这个Handlerlnterceptor接口。
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
//以下是重写HandlerInterceptor中的方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object uid = request.getSession().getAttribute("uid");
if (uid == null){
response.sendRedirect("/web/login.html");
return false; //返回false拦截
}
return true;//返回true放行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
2.注册过滤器:添加白名单(哪些资源可以在不登录的情况下访问:login.htmI、register.htm、login、reg、lindex.html、product.html、添加黑名单(在用户登录的状态才可以访问的页面资源)。
3.注册过滤器的技术:借助WebMvcConfigure接口,可以将用户定义的拦截器进行注册,才可以保证拦截器能够生效和使用。定义一个类,然后让这个类实现WebMvcConfigure接口。配置信息,建议存放在项目的config包结构下。
import com.cy.store.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class LoginInterceptConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
List<String> patterns = new ArrayList<>();
patterns.add("/users/login");
patterns.add("/users/reg");
patterns.add("/bootstrap3/**");
patterns.add("/css/**");
patterns.add("/images/**");
patterns.add("/js/**");
patterns.add("/web/login.html");
patterns.add("/web/register.html");
patterns.add("/web/product.html");
patterns.add("/index.html");
LoginInterceptor loginInterceptor = new LoginInterceptor();
//excludePathPatterns()是白名单,参数是一个集合
registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns(patterns);
}
}
4.提示重定向次数过多,login.html页面无法打开。将浏览器cookie请求,再将浏览器设置为初始设置。
mybatis.mapper-locations=classpath:mapper/*.xml
console.log(avatar);
文件下载的几种方法
1. 将文件以流的形式一次性读取到内存,通过响应输出流输出到前端
/**
* @param path 想要下载的文件的路径
* @param response
* @功能描述 下载文件:
*/
@RequestMapping("/download")
public void download(String path, HttpServletResponse response) {
try {
// path是指想要下载的文件的路径
File file = new File(path);
log.info(file.getPath());
// 获取文件名
String filename = file.getName();
// 获取文件后缀名
String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
log.info("文件后缀名:" + ext);
// 将文件写入输入流
FileInputStream fileInputStream = new FileInputStream(file);
InputStream fis = new BufferedInputStream(fileInputStream);
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
// 清空response
response.reset();
// 设置response的Header
response.setCharacterEncoding("UTF-8");
//Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
//attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
// filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
// 告知浏览器文件的大小
response.addHeader("Content-Length", "" + file.length());
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
outputStream.write(buffer);
outputStream.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
}
2. 将输入流中的数据循环写入到响应输出流中,而不是一次性读取到内存,通过响应输出流输出到前端
/**
* @param path 指想要下载的文件的路径
* @param response
* @功能描述 下载文件:将输入流中的数据循环写入到响应输出流中,而不是一次性读取到内存
*/
@RequestMapping("/downloadLocal")
public void downloadLocal(String path, HttpServletResponse response) throws IOException {
// 读到流中
InputStream inputStream = new FileInputStream(path);// 文件的存放路径
response.reset();
response.setContentType("application/octet-stream");
String filename = new File(path).getName();
response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
ServletOutputStream outputStream = response.getOutputStream();
byte[] b = new byte[1024];
int len;
//从输入流中读取一定数量的字节,并将其存储在缓冲区字节数组中,读到末尾返回-1
while ((len = inputStream.read(b)) > 0) {
outputStream.write(b, 0, len);
}
inputStream.close();
}
3. 下载网络文件到本地
/**
* @param path 下载后的文件路径和名称
* @param netAddress 文件所在网络地址
* @功能描述 网络文件下载到服务器本地
*/
@RequestMapping("/netDownloadLocal")
public void downloadNet(String netAddress, String path) throws IOException {
URL url = new URL(netAddress);
URLConnection conn = url.openConnection();
InputStream inputStream = conn.getInputStream();
FileOutputStream fileOutputStream = new FileOutputStream(path);
int bytesum = 0;
int byteread;
byte[] buffer = new byte[1024];
while ((byteread = inputStream.read(buffer)) != -1) {
bytesum += byteread;
System.out.println(bytesum);
fileOutputStream.write(buffer, 0, byteread);
}
fileOutputStream.close();
}
4. 网络文件获取到服务器后,经服务器处理后响应给前端
/**
* @param netAddress
* @param filename
* @param isOnLine
* @param response
* @功能描述 网络文件获取到服务器后,经服务器处理后响应给前端
*/
@RequestMapping("/netDownLoadNet")
public void netDownLoadNet(String netAddress, String filename, boolean isOnLine, HttpServletResponse response) throws Exception {
URL url = new URL(netAddress);
URLConnection conn = url.openConnection();
InputStream inputStream = conn.getInputStream();
response.reset();
response.setContentType(conn.getContentType());
if (isOnLine) {
// 在线打开方式 文件名应该编码成UTF-8
response.setHeader("Content-Disposition", "inline; filename=" + URLEncoder.encode(filename, "UTF-8"));
} else {
//纯下载方式 文件名应该编码成UTF-8
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
}
byte[] buffer = new byte[1024];
int len;
OutputStream outputStream = response.getOutputStream();
while ((len = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, len);
}
inputStream.close();
}
<!-- 根据若干个购物车数据id查询详情的列表:List<CartVO> findVOByCids(Integer[] cids) -->
<select id="findVOByCids" resultType="com.lll.store.vo.CartVO">
SELECT
cid,
uid,
pid,
t_cart.price,
t_cart.num,
t_product.title,
t_product.price AS realPrice,
t_product.image
FROM
t_cart
LEFT JOIN t_product ON t_cart.pid = t_product.id
WHERE
cid IN (
<foreach collection="array" item="cid" separator=",">
#{cid}
</foreach>
)
ORDER BY
t_cart.created_time DESC
</select>
function showCartList() {
$("#cart-list").empty();
// cid=6&cid=5&cid=4
// console.log(location.search.substr(1));
$.ajax({
url: "/carts/list",
//location.search 代表访问此页面的路径,location.search.substr(0) 路径中?前面的部分
//location.search.substr(1) ?后面的部分
data: location.search.substr(1),
type: "GET",
dataType: "JSON",
success: function(json) {
let list = json.data;
// console.log("count=" + list.length);
let allCount = 0;
let allPrice = 0;
console.log(list.length);
for (let i = 0; i < list.length; i++) {
console.log(list[i].title);
let tr = '<tr>'
+ '<td><img src="..#{image}collect.png" class="img-responsive" /></td>'
+ '<td><input type="hidden" name="cids" value="#{cid}" />#{title}</td>'
+ '<td>¥<span>#{realPrice}</span></td>'
+ '<td>#{num}</td>'
+ '<td>¥<span>#{totalPrice}</span></td>'
+ '</tr>';
tr = tr.replace(/#{cid}/g, list[i].cid);
tr = tr.replace(/#{image}/g, list[i].image);
tr = tr.replace(/#{title}/g, list[i].title);
tr = tr.replace(/#{realPrice}/g, list[i].realPrice);
tr = tr.replace(/#{num}/g, list[i].num);
tr = tr.replace(/#{totalPrice}/g, list[i].realPrice * list[i].num);
$("#cart-list").append(tr);
allCount += list[i].num;
allPrice += list[i].realPrice * list[i].num;
}
$("#all-count").html(allCount);
$("#all-price").html(allPrice);
}
});
}