目录:
1、day01-AOP
2、day02-HTTP文件的上载和下载
3、day03-Ajax 文件上载
4、密码加密
1、day01-AOP
面向切面(儿)编程:面向程序的横截面(儿)编程,将软件横向切开,在原有软件不变的情况下扩展横向的功能。
AOP:
如何没有AOP将会是这样的:
你好,世界
AOP的演示案例:
步骤:
-
导入包:
-
aspectjweaver:(汉)编辑切面;
aspectjtools:(汉)切面工具
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>3.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjtools</artifactId> <version>1.8.0</version> </dependency>
-
开发了AOP Bean组件
@Component //Component:(汉)组成构成;将DemoAspect交给Spring管理 @Aspect //声明当前Bean组件是一个切面组件 public class DemoAspect { /** * @Before("bean(userService)") 注解的意义: * 在userService bean的全部方法之前执行test 方法, * 这个注解声明会被Spring 自动处理,并且执行 */ @Before("bean(userService)") public void test(){ System.out.println("Hello World!"); } }
-
配置:
<!-- spring-aop.xml --> <context:component-scan base-package="cn.tedu.store.aspect" /> <!-- 让 aspectj 带来的 注解生效--> <aop:aspectj-autoproxy/>
通知
用于声明AOP方法在目标业务方法执行时机。
常用的有5种通知:
@Before //(汉)在**方法之前;的全部方法之前执行。
@After //(汉)在**方法之后;的全部方法之后执行。
@AfterThrowing //(汉)在**方法之后后抛;在目标方法有异常情况下抛出。
@AfterReturning //(汉)在**方法之后回归;目标方法正常执行之后执行。
@Around //(汉)周围,环绕
执行原理如下:
@Before 在**之前
在目标方法之前执行,案例:
/**
* @Before("bean(userService)") 注解的意义:
* 在userService bean的全部方法之前执行test 方法,
* 这个注解声明会被Spring 自动处理,并且执行
* Before 之前
*/
@Before("bean(userService)")
public void test(){
System.out.println("Hello World!");
}
@After 在**之后
在目标方法执行之后执行,无论目标业务方法是否出现异常@After修饰的方法都会执行:
/**
* 在 userService 的全部方法之后(After)执行
*/
@After("bean(userService)")
public void test2(){
System.out.println("Hello @After!");
}
@AfterReturning接口
在目标方法正常执行之后,如果没有异常发生,则执行@AfterReturning接口修饰的方法:
/**
* 在目标方法没有异常情况下执行
*/
@AfterReturning("bean(userService)")
public void test3(){
System.out.println("Hello @AfterReturning");
}
@AfterThrowing
在目标方法执行出现异常以后会执行@AfterThrowing修饰的方法。
/**
* 在目标方法有异常情况下执行
*/
@AfterThrowing("bean(userService)")
public void test4(){
System.out.println("Hello @AfterThrowing");
}
@Around 通知; (汉)周围;
这个是一个万能通知,可以替代其他几个通知,但是使用繁琐:
案例:
/**
* Around 通知: 对应的AOP方法:
* 1. 必须有 参数 ProceedingJoinPoint
* 2. 必须有返回值 Object
* 3. 必须抛出异常 Throwable
* @param jp
* @return
* @throws Throwable
*/
@Around("bean(userService)")
public Object test5(ProceedingJoinPoint jp)
throws Throwable{
//Proceeding 进行,处理
//Join 连接
//Point 点
// 处理过程的连接点
System.out.println("Around Before");
//jp.proceed() 调用了目标业务方法,其返回值
//就是业务方法返回的业务处理结果
Object obj = jp.proceed();
//jp 对象中包含被调用目标方法的全部信息
//其中 getSignature 返回方法的签名,包括:
//方法和方法的参数类型列表
Signature method= jp.getSignature();
System.out.println(method);
System.out.println("Around After:"+obj);
return obj;
}
切入点表达式切入点
切入点用于声明对那些类,对象,方法进行AOP切入
豆组件切入点
语法:
@Before("bean(userService)") //切入到userService的全部方法
@Before("bean(userService) || bean(dictService)")
案例:
/**
* Bean组件切入点,对两个Bean组件进行切入
* 如下程序将在 userService 和 dictService
* 全部方法之前执行 test() 方法
*/
//@Before("bean(userService) || bean(dictService)")
public void test(){
System.out.println("Point Cut Test!");
}
/**
* 切入点表达式 bean(*Service) 将会拦截全部的
* 业务层方法,每个业务层方法之前都会执行 test1()
* 方法。使用这种方式进行切入时候,建议有统一的
* 命名规范。
*/
//@Before("bean(*Service)")
public void test1(){
System.out.println("Point Cut Test1!");
}
类切入点
按照具体的类名,切到类的全部方法
语法:
//切入到类 UserServiceImpl 中声明的全部的方法
@Before("within(cn.tedu.store.service.UserServiceImpl)")
@Before("within(cn.tedu.store.service.*ServiceImpl)")
@Before("within(cn.tedu.store..*Impl)")
案例:
/**
* 类的切入点:如下切入点表达式将test2()方法切入到
* UserServiceImpl 的全部方法之前执行。
*/
@Before("within(cn.tedu.store.service.UserServiceImpl)")
public void test2(){
System.out.println("Point Cut Test2!");
}
@Before("within(cn.tedu.store.service.*ServiceImpl)")
public void test3(){
System.out.println("Point Cut Test3!");
}
方法切入点
语法:
//execution 执行
@Before("execution(修饰词 类名.方法名(参数类型))")
@Before("execution(* cn.tedu.store.service.UserService.login(..))")
@Before("execution(* cn.tedu.store.service.*Service.get*(..))")
@Before("execution(* cn.tedu.store..*Service.get*(..))")
案例:
/**
* 方法切入点:只切入到login方法
*/
@Before("execution(* cn.tedu.store.service.*Service.login(..))")
public void test4(){
System.out.println("Point Cut Test4!");
}
测试业务层的所有方法的性能
方案:
@Component
@Aspect
public class TestAspect {
@Around("bean(*Service)")
public Object test(ProceedingJoinPoint jp)
throws Throwable{
try {
long t1 = System.currentTimeMillis();
Object val=jp.proceed();
long t2 = System.currentTimeMillis();
Signature m = jp.getSignature();
System.out.println((t2-t1)+":"+m);
return val;
} catch (Throwable e) {
//继续抛出业务异常
throw e;
}
}
}
AOP的工作原理
Spting底层是AspectJ再底层利用了Java的反射和动态代理。其中反射用于解析注解,动态代理用于生成代理对象。
Spring AOP的底层代理方式有两种,一种是基于Java动态代理对象,一种是基于CGlib的动态代理。当前代理对象有接口时候优先使用JAVA动态代理,如果被代理对象没有接口时候,会自动使用CGLIB。推荐使用JAVA动态代理。
2、day02-HTTP文件的上载和下载
/**
* 生成验证码图片控制器
* 其中value="code.do" 用于映射URL
*
* consumes="image/png"用于设置响应头中的 Content-Type属性
* 返回值byte[]会被注解@ResponseBody自动处理放置到响应消息
* 的Body中发送到客户端
*
* @ResponseBody还会根据 byte[]数组长度自动设置响应头部的
* Content-Length属性
*
* @return
* @throws IOException
*/
@RequestMapping(value="code.do",produces="image/png")
@ResponseBody
public byte[] code() throws IOException {
String code = getCode(4);
byte[]png =createPng(code);
return png;
}
private byte[] createPng(String code) throws IOException {
//1.利用BufferedImage 创建img对象;BufferedImage 子类描述具有可访问图像数据缓冲区的 Image
BufferedImage img = new BufferedImage(100, 40,BufferedImage.TYPE_3BYTE_BGR);
//设置坐标为(50,20)的灯亮
img.setRGB(50, 20, 0xffff00);
//graphics:(汉)绘制学
Graphics2D g =img.createGraphics();
//生成随机色
Random random = new Random ();
Color c = new Color(random.nextInt(0xffffff));
g.setColor(c);
//填充矩形
g.fillRect(0, 0, 100, 40);
//绘制300个随机点
for(int i=0;i<300;i++) {
int x = random.nextInt(100);
int y = random.nextInt(40);
int rgb = random.nextInt(0xffffff);
img.setRGB(x, y, rgb);
}
//设置平滑抗锯齿绘制
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//设置字体大小
g.setFont(new Font(Font.SANS_SERIF,Font.PLAIN,30));
//设置字体随机色
g.setColor(new Color(random.nextInt(0xffffff)));
//画字
g.drawString(code, 10, 30);
//在字体上乱画线
for(int i=0;i<10;i++) {
int x1=random.nextInt(100);
int y1=random.nextInt(40);
int x2=random.nextInt(100);
int y2=random.nextInt(40);
g.drawLine(x1, y1, x2, y2);
}
//2.利用imageIO将img编码png
/**
* 此类实现了一个输出流,其中的数据被写入一个 byte 数组。
* 缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray()
* 和 toString() 获取数据。
* 关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,
* 而不会产生任何 IOException。
*/
ByteArrayOutputStream out = new ByteArrayOutputStream();//酱油瓶子
ImageIO.write(img, "png", out);//将酱油装进瓶子
out.close();
byte[]bytes = out.toByteArray();//将酱油倒出
return bytes;
}
public byte[] createPng2() throws IOException {
//缓冲图片
BufferedImage img = new BufferedImage (100,40,BufferedImage.TYPE_3BYTE_BGR);
//把其中的图片的灯打开
for(int x = 0 ;x<100;x++) {
for(int y=0;y<40;y++) {
img.setRGB(x, y, 0xff0000);
}
}
//容器
ByteArrayOutputStream out = new ByteArrayOutputStream();
//将缓冲图片放进瓶子,并转换成png格式
ImageIO.write(img, "png", out);
out.close();
//将转好格式的图片写出
byte [] bytes = out.toByteArray();
return bytes;
}
private static String chs = "3456789abcdefghigkmnopqrstwxy";
public String getCode(int nub) {
char[] code =new char[nub];
Random random = new Random();
for(int i=0;i<code.length;i++) {
code[i] =chs.charAt(random.nextInt(chs.length()));
}
return new String (code);
}
HTTP文件的上载和下载
HTTP下载
HTTP协议下载关键点:
-
设置响应状态码为200
-
设置响应头Content-Type
-
设置响应头内容长度
-
在响应正文中发送下载字节数据,数据内容必须与内容类型一致,数据长度必须与内容长度一致。
原理:
Spring MVC下载
用SpringMVC对资源下载做了封装:
-
利用@RequestMapping(生产= “图像/ PNG”)设置的内容类型
-
利用@ResponseBody和控制器方法byte []类型的返回值填充响应Body,并且自动设置Content-Length
生成验证码图片案例:
/**
* 生成验证码图片控制器
* 其中 value="code.do"用于映射URL
* produces="image/png" 用于设置响应头中的
* Content-Type 属性
* 返回值 byte[] 会被注解@ResponseBody自动
* 处理放置到响应消息的Body中发送到客户端
*
* @ResponseBody 还会根据 byte[] 数组长度自动
* 设置 响应头部的Content-Length属性
*
* @return
*/
@RequestMapping(value="code.do",
produces="image/png")
@ResponseBody
public byte[] code(HttpSession session)
throws IOException{
String code = genCode(4);
session.setAttribute("code", code);
byte[] png = createPng(code);
return png;
}
private byte[] createPng(String code) throws IOException{
//1. 利用BufferedImage 创建 img 对象
BufferedImage img=new BufferedImage
(100, 40, BufferedImage.TYPE_3BYTE_BGR);
img.setRGB(50, 20, 0xffffff);
Graphics2D g=img.createGraphics();
Random random = new Random();
//生成随机颜色:
Color c=new Color(random.nextInt(0xffffff));
//填充图像的背景
g.setColor(c);
g.fillRect(0, 0, 100, 40);
//绘制500个随机点
for(int i=0; i<500; i++){
int x=random.nextInt(100);
int y=random.nextInt(40);
int rgb=random.nextInt(0xffffff);
img.setRGB(x, y, rgb);
}
//设置平滑抗锯齿绘制
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
//设置字体大小
g.setFont(new Font(Font.SANS_SERIF,
Font.PLAIN, 30));
g.setColor(new Color(
random.nextInt(0xffffff)));
g.drawString(code, 10, 30);
//随机绘制10条线段
for(int i=0; i<10; i++){
int x1=random.nextInt(100);
int y1=random.nextInt(40);
int x2=random.nextInt(100);
int y2=random.nextInt(40);
g.drawLine(x1, y1, x2, y2);
}
//2. 利用ImageIO将 img 编码为png
ByteArrayOutputStream out=
new ByteArrayOutputStream();
ImageIO.write(img, "png", out);
out.close();
byte[] bytes=out.toByteArray();
return bytes;
}
private static String chs=
"345678abcdefhjkmnpqrstuvwxyABCDEFGHJL";
private String genCode(int len){
char[] code=new char[len];
Random random = new Random();
for(int i=0; i<code.length; i++){
code[i]=chs.charAt(
random.nextInt(chs.length()));
}
return new String(code);
}
检验验证码原理:
步骤:
-
login.jsp的中添加IMG标签显示图片:
<div class="text"> <input type="text" id="code" placeholder="请输入验证码" name="code" required minlength="4" maxlength="4"> <span><img id="code_image" style="top:-39px;right:-155px" src="code.do"></span> </div>
-
更新login.css显示位置
#cover .txt{ float: left; overflow: hidden; width: 253px; /* height: 277px; 避免遮挡验证码 */ padding: 10px; }
-
发起AJAX请求验证
$("#code").blur(function(){ var data = $("#code").val(); console.log(data); if (data == null || data == "") { $("#showResult").text("验证码不能为空!"); $("#showResult").css("color","red"); return false; } $.ajax({ "type":"POST", "url":"checkCode.do", "data":"code="+data, "beforeSend":function(XMLHttpRequest){ $("#showResult").text("正在查询..."); $("#showResult").css("color", "green"); }, "success":function(obj) { var color = obj.state == 1 ? "green" : "red"; $("#showResult").css("color", color); $("#showResult").text(obj.message); }, "error":function() { //错误处理 } }); });
-
添加更新点击更新图片的方法
//点击验证码图片时候更新图片$(“#code_image”)。click(function(){var img = this; console.log(img); //添加请求参数的目的避免浏览器的缓存img.src =“code.do?”+ new Date()。getTime();});
-
添加检验验证码控制器方法
/** * 检查验证码 */ @RequestMapping("checkCode.do") @ResponseBody public ResponseResult<Void> checkCode( String code, HttpSession session){ ResponseResult<Void> rr= new ResponseResult<Void>(); String c = (String)session.getAttribute("code"); if(c !=null && c.equalsIgnoreCase(code)){ rr.setState(ResponseResult.STATE_OK); rr.setMessage("验证码检查通过"); }else{ rr.setState(ResponseResult.STATE_ERROR); rr.setMessage("验证码错误"); } return rr; }
-
放过验证请求spring-mvc.xml:
<mvc:exclude-mapping path="/user/code.do"/> <mvc:exclude-mapping path="/user/checkCode.do"/>
-
测试....
自动下载图片功能
HTTP协议中如果了下载属性头内容处置,设置这个响应头,就可以实现下载保存到文件中:
步骤:
-
编写控制器方法
/*** 弹框提示下载图片* Content-Disposition: attachment; filename="ok.png"* 参考:REF* @throws IOException*/@RequestMapping(value = "/downloadImage.do", produces="image/png")
@ResponseBody
public byte[] downloadImage(HttpServletResponse response) throws IOException { System.out.println("下载图片");
response.setHeader("Content-Disposition",
"attachment;filename=\"ok.png\"");
byte [] bytes = createPng("OK");
return bytes;
}
private byte[] createPng(String code) throws IOException {
//1.利用BufferedImage 创建img对象;BufferedImage 子类描述具有可访问图像数据缓冲区的 Image
BufferedImage img = new BufferedImage(100, 40,
BufferedImage.TYPE_3BYTE_BGR);
//设置坐标为(50,20)的灯亮
img.setRGB(50, 20, 0xffff00);
//graphics:(汉)绘制学
Graphics2D g =img.createGraphics();
//生成随机色
Random random = new Random ();
Color c = new Color(random.nextInt(0xffffff));
//设置背景色
g.setColor(c);
//fillRect:(汉)填充矩形;
g.fillRect(0, 0, 100, 40);
//绘制300个随机点
for(int i=0;i<100;i++) {
int x = random.nextInt(100);
int y = random.nextInt(40);
int rgb = random.nextInt(0xffffff);
img.setRGB(x, y, rgb);
}
//设置平滑抗锯齿绘制;RenderingHint打底示意
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
//设置字体大小 sans serif:字体 plain:平滑的
g.setFont(new Font(Font.SANS_SERIF,Font.PLAIN,30));
//设置字体随机色
g.setColor(new Color(random.nextInt(0xffffff)));
//画字; 从字符的位置(10,30)开始画字符
g.drawString(code,10, 30);
//在字体上乱画线
for(int i=0;i<10;i++) {
int x1=random.nextInt(100);
int y1=random.nextInt(40);
int x2=random.nextInt(100);
int y2=random.nextInt(40);
g.drawLine(x1, y1, x2, y2);
}
//2.利用imageIO将img编码png
/**
* 此类实现了一个输出流,其中的数据被写入一个 byte 数组。
* 缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray()
* 和 toString() 获取数据。
* 关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,
* 而不会产生任何 IOException。
*/
ByteArrayOutputStream out = new ByteArrayOutputStream();//酱油瓶子
ImageIO.write(img, "png", out);//将酱油装进瓶子
out.close();
byte[]bytes = out.toByteArray();//将酱油倒出
return bytes;
}
与下载PNG图片的原理一样,只是的ContentType的值不同:
1、导入POI API
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.16</version>
</dependency>
2、编写控制器方法
/**
* 下载Excel
* 去Servers里的web.xml中找到produces对应的产品属性
* xlsx
* application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
*
* attachment:(汉)连接物,依附
*
*/
@RequestMapping(value = "/downloadExcel.do",
produces="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
@ResponseBody
public byte[] downloadExcel(HttpServletResponse response) throws IOException {
System.out.println("下载Excel表");
response.setHeader("Content-Disposition", "attachment; filename=\"ok.xlsx\"");
byte [] bytes = createExcel();
return bytes;
}
/**
* 利用Excel API poi创建Excel对象
* @return
*/
@SuppressWarnings("resource")
private byte[] createExcel() throws IOException{
//创建工作薄
XSSFWorkbook workbook = new XSSFWorkbook();
//在工作表中添加工作表;Sheet:(汉)片
XSSFSheet sheet1 = workbook.createSheet("花名册");
//在工作表中添加两行 Row:(汉)行
XSSFRow head = sheet1.createRow(0);
XSSFRow row = sheet1.createRow(1);
//第一行作为表头;createCell(汉)创建一个小格子
XSSFCell c0 = head.createCell(0);
//将创建的一个格子添加“编号”属性
c0.setCellValue("编号");
head.createCell(1).setCellValue("姓名");
head.createCell(2).setCellValue("年龄");
row.createCell(0).setCellValue(1);
row.createCell(1).setCellValue("范传奇");
row.createCell(2).setCellValue(34);
//将Excel对象保存为bytes
ByteArrayOutputStream out = new ByteArrayOutputStream();
//将这个工作薄写入到字节数组输出流里
workbook.write(out);
out.close();
byte [] bytes = out.toByteArray();
return bytes;
}
3、添加客户端链接
<a href="downloadExcel.do">Excel</a>
4、测试
上载文件
HTTP上载是基于RFC 1867标准,Spring MVC利用Apache commons fileupload组件支持了这个标准,这样利用Spring MVC提供的API可以轻松的获得上载文件:
参考Spring 3.2 手册: 17.10 Spring's multipart (file upload) support
表单上载
实现上载步骤:
-
导入 commons fileupload 上载组件
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency>
-
在spring-MVC中配置Spring 上载解析器:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="5000000"/> <property name="defaultEncoding" value="utf-8"></property> </bean>
-
编写表单 web/upload.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>文件上载</title> </head> <body> <h1>文件上载</h1> <form enctype="multipart/form-data" action="upload.do" method="post"> 姓名:<input type="text" name="username"><br> 照片:<input name="userfile1" type="file"><br> <input type="submit" value="上载"> </form> </body> </html>
-
编写控制器方法显示表单
/** * 显示上载表单 */ @RequestMapping("uploadForm.do") public String uploadForm(){ return "upload"; }
-
编写控制器处理上载请求
/** * 处理上载请求 */ @RequestMapping(value="upload.do", method=RequestMethod.POST) @ResponseBody public ResponseResult<Void> upload( @RequestParam("userfile1") MultipartFile image, @RequestParam("username") String username, HttpServletRequest request) throws IOException { //打桩输出上载结果 System.out.println(username); System.out.println(image); //获取上载文件信息 System.out.println(image.getContentType()); System.out.println(image.getName()); System.out.println(image.getOriginalFilename()); System.out.println(image.getSize()); //保存到文件系统 String path="/images/upload";//WEB路径 path = request.getServletContext() .getRealPath(path); System.out.println(path); //创建upload文件夹 File dir = new File(path); dir.mkdir(); File file=new File(dir, image.getOriginalFilename()); //将上载文件保存到文件中 image.transferTo(file); ResponseResult<Void> rr=new ResponseResult<Void>(); rr.setState(ResponseResult.STATE_OK); rr.setMessage("上载成功"); return rr; }
-
测试: 先显示上载表单,然后选择文件上载到服务器
上载后可以使用URL显示上载的文件,如: http://localhost:8080/TeduStore/images/upload/Chrysanthemum.jpg
作业
1,将用户信息打包为Excel文件下载
2,实现将2个图片和图片描述信息上载到服务器
3、day03-Ajax 文件上载
XHR2 XMLHttpRequest2
XHR2提供了异步文件上载功能。
使用步骤是:
-
利用 <input type="file"> 对象选择获取文件对象
-
创建 FormData 对象用于承载上传文件。
-
将文件对象插入到 FormData 对象 中
-
将 FormData 对象利用Ajax发送到服务器。
JS Ajax 上载
开发步骤:
-
创建上载页面元素:
<h2>ajax 上载</h2> <div> <!-- 指定 multiple="multiple" 属性,可以选择 多个文件 --> <label>选择图片</label> <input type="file" id="images" multiple="multiple"><br> <input id="ajax_upload" type="button" value="ajax上载"> </div> <h3>选择了:</h3> <div id="selected"> </div>
-
编写脚本显示准备上载的图片:
var images = document.getElementById("images"); var selected=document.getElementById("selected"); images.onchange=function(){ var files = this.files; selected.innerHTML=""; for(var i=0; i<files.length; i++){ var f = files[i]; var url = window.URL.createObjectURL(f); var img = new Image(); img.src = url; selected.appendChild(img); } };
-
编写Ajax上载脚本:
var btn=document.getElementById("ajax_upload"); btn.onclick=function(){ var files = images.files; var frm = new FormData();//空白表单 //将文件添加到frm中 for(var i=0; i<files.length; i++){ var f = files[i]; frm.append("images", f, f.name); } //发起Ajax请求 btn.value = "上载中..."; var xhr = new XMLHttpRequest(); xhr.open("post", "uploadImages.do"); xhr.onreadystatechange = function(){ if(xhr.readyState==4&&xhr.status==200){ var json=JSON.parse(xhr.responseText); console.log(json); selected.innerHTML=json.message; btn.value = "ajax上载"; } }; xhr.send(frm); };
-
编写服务器控制器接收文件上载 /** * 处理上载请求, 保存多个文件 */ @RequestMapping(value=" uploadImages.do ", method=RequestMethod.POST) @ResponseBody public ResponseResult uploadImages( @RequestParam("images") MultipartFile[] images, HttpServletRequest request) throws IOException { //保存到文件系统 String path="/images/upload";//WEB路径 path = request.getServletContext() .getRealPath(path); System.out.println(path); //创建upload文件夹 File dir = new File(path); dir.mkdir(); for (MultipartFile image : images) { File file=new File(dir, image.getOriginalFilename()); System.out.println("save:"+file); //将上载文件保存到文件中 image.transferTo(file); }
ResponseResult<Void> rr=new ResponseResult<Void>(); rr.setState(ResponseResult.STATE_OK); rr.setMessage("上载成功"); return rr; }
-
测试
JQuery Ajax 上载
利用JQuery也可以实现Ajax文件上载
-
编写界面
<h2>JQuery 上载</h2> <div> <label>选择图片</label> <input type="file" id="photos" multiple="multiple"><br> <input id="jquery_upload" type="button" value="jquery上载"> </div> <h3>选择了:</h3> <div id="selected_photos"> </div>
-
编写脚本显示准备上载的图片
var photos=$("#photos"); var selectedPhotos=$("#selected_photos"); photos.change(function(){ var files=this.files; selectedPhotos.empty(); for(var i=0; i<files.length; i++){ var f = files[i]; var url = window.URL.createObjectURL(f); var img = $("<img src='"+url+"'>"); selectedPhotos.append(img); } });
-
编写上载脚本:
var ajaxBtn=$("#jquery_upload"); ajaxBtn.click(function(){ var url="uploadImages.do"; var data=new FormData(); var files = photos[0].files; for(var i=0; i<files.length; i++){ var f = files[i]; data.append("images", f, f.name); } ajaxBtn.val("上载中..."); $.ajax({ url:url, type:"POST", data:data, dataType:"json", processData:false, //不要处理 data数据!!! contentType:false, //不要有JQuery设定ContentType success:function(json){ ajaxBtn.val("JQuery 上载"); selectedPhotos.html(json.message); } }); });
-
测试
4、密码加密技术(消息摘要技术)
消息摘要: 利用散列算法抽取一组数据的特征,做为这个数据的唯一标识,相当于这组数据的“指纹”
一组数据 -- MD5、SHA散列算法 --> 消息摘要
同样的数据原文具有相同的摘要,不同的原文具有不同的摘要。
消息摘要的用途: 用于检验数据的完整性!
原理:
消息摘要技术广泛用在软件中,用于比较数据的一致性和完整性。
-
迅雷使用消息摘要技术比较下载文件的完整性
-
各种网盘的秒传功能就是利用消息摘要来确认需要上载文件是否与服务器已有文件一致,如果一致就秒传了!
密码加密原理:
消息摘要与密码加密
-
消息摘要是一种检验数据完整性和一致性的技术
-
消息摘要的用途广泛,用途之一是 密码加密
-
消息摘要算法有很多种:MD2 MD5 SHA SHA-1 SHA-256 SHA-384 等
-
常用的有 MD5 SHA
-
-
消息摘要算法是单向算法,只能根据数据计算摘要,无法反向计算!
-
有摘要反向查询网址,提供了简单字符串的摘要查询!
-
不是反向计算解密的结果!
-
-
利用摘要加密密码时候,检验使用"加盐"处理,避免反向查询。
修改数据表的列
alter table t_user modify password varchar(50);
消息再要案例:
public class TestCase {
@Test
public void testMd5() throws Exception{
String file = "passwd";
FileInputStream in=new FileInputStream(file);
//计算文件的摘要
//linux 计算摘要命令: md5sum passwd
//mac 计算摘要命令: md5 passwd
String md5=DigestUtils.md5Hex(in);
System.out.println(md5);
in.close();
}
@Test
public void testStringMd5(){
String password="1234";
String md5=DigestUtils.md5Hex(password);
System.out.println(md5);
String salt="今天你吃了吗?";
md5 = DigestUtils.md5Hex(password+salt);
System.out.println(md5);
md5 = DigestUtils.md5Hex(salt+password);
System.out.println(md5);
}
}
更新项目实行密码加密功能:
-
更新配置文件添加“盐”:
<!-- 使用表达式 #{config.salt}可以读取值 --> <util:properties id="config"> <prop key="salt">今天你吃了吗?</prop> </util:properties>
-
更新UserServiceImpl,增加密码加密和加密验证功能:
//从Spring中找到一个BeanID为config的bean,获取 //其salt属性的值,注入到salt变量中 @Value("#{config.salt}") private String salt; public void register( String username, String password, String phone, String email) { User user = new User(); Date now = new Date(); user.setUsername(username); //密码摘要加密 System.out.println(salt); String pwd=DigestUtils.md5Hex(password+salt); System.out.println(pwd); user.setPassword(pwd); user.setPhone(phone); user.setEmail(email); user.setDisabled(0); user.setCreatedTime(now); user.setCreatedUser("System"); user.setModifiedTime(now); user.setModifiedUser("System"); register(user); } @Transactional public User login(String username, String password) { //为了测试AOP功能添加的 测试代码 System.out.println("登录功能"); if(username.equals("chenghen2")){ System.out.println("异常"); throw new RuntimeException("出异常了"); } User u = findUserByUsername(username); if (u == null) { return null; } else { //比较摘要加密以后的密码 String pwd=DigestUtils.md5Hex(password+salt); if (u.getPassword().equals(pwd)) { return u; } else { return null; } } } public int updatePassword(Integer uid, String oldPassword, String newPassword) { // 初始化:参数、返回值 int state = -1; // 处理业务 // 根据uid获取用户信息 User user = userMapper.findUserById(uid); // 判断用户输入的旧密码是否正确 String pwd=DigestUtils.md5Hex(oldPassword+salt); if (user.getPassword().equals(pwd)) { // 密码正确,允许修改 pwd = DigestUtils.md5Hex(newPassword+salt); userMapper.updatePassword(uid, pwd); // 给正确的返回值 state = 1; } // 返回 return state; }