2.常用功能(分页、文件上传下载_swagger工具_验证码实现)
常用功能合集
1、分页功能实现
1.首先layui开启分页
(一般在table.render里面使用,如果不加参数,默认limit=10,page=1)
,limits: [5, 10, 15, 20, 50, 100] //每页条数的选择项。如果 layout 参数开启了 limit,则会出现每页条数的select选择框
,limit: 5 //每页显示的条数。laypage将会借助 count 和 limit 计算出分页数。
,page: true //是否显示分页
其他参数可以参考layui官方文档——内置模块——分页layerpage的介绍
2.pom.xml配置
<!-- pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<!-- 注意:spring boot 引入的jar包必须是要pagehelper-spring-boot-starter ,如果单独引入pagehelper的话,会提示错误。 -->
3.application.yml增加pagehelper 配置
#分页框架
pagehelper:
helper-dialect: mysql
reasonable: true
supportMethodsArguments: true
helperDialect : 指定数据库,可以不配置,pagehelper插件会自动检测数据库的类型。
resonable : 分页合理化参数默认false,当该参数设置为true 时,pageNum <= 0 时,默认显示第一页,pageNum 超过 pageSize 时,显示最后一页。
supportMethodsArguments : 分页插件会根据查询方法的参数中,自动根据params 配置的字段中取值,找到合适的值会自动分页。
4.在控制器中实现分页
@GetMapping("/getProductList")
@ResponseBody
public Response list(@RequestParam int limit, @RequestParam int page, Product product){
//limit是每页的条数,page是标识第几页
PageHelper.startPage(page, limit);
List<Product> list = productService.selectProductList(product);
PageInfo pageInfo = new PageInfo(list,limit);
// response.setStatusCode(1);
// response.setStatusMsg("");
// response.setCount(pageInfo.getTotal());
// response.setDetailList(pageInfo.getList());
response = getDataTable(pageInfo,"");
//可以选择用上面的四句,也可以把这几句封装成一个方法直接调用
return response;
}
分页的核心就一行代码, PageHelper.startPage(page,pageSize); 这个就标识开始分页。加了这个之后pagehelper 插件就会通过其内部的拦截器,将执行的sql语句,转化为分页的sql语句。
注意:使用时PageHelper.startPage(pageNum, pageSize)一定要放在列表查询的方法中,这样在查询时会查出相应的数据量且会查询出总数。
2、文件上传
参考网址
-
这个大佬讲后端比较清楚:https://blog.csdn.net/justry_deng/article/details/80855235
-
这个大佬涉及前端的东西讲的多一点:https://www.cnblogs.com/lunaticcoder/p/9813483.html:
-
结合着看就差不多了
SpringMVC使用MultipartFile上传文件
MultipartFile介绍
MultipartFile是springmvc官方提供的一个比较完善的文件上传组件,MultipartFile是一个组织接口,它的实现类有
- org.springframework.web.multipart.commons.CommonsMultipartFile
- org.springframework.mock.web.MockMultipartFile
它在springmvc中的org.springframework.web.multipart这个包内,与org.springframework.web.multipart. commons和org.springframework.web.multipart.support包内的类接口组合使用该。
单文件上传
前端
<form method="post" action="http://localhost:8888/file/upload" enctype="multipart/form-data">
<input name = "fileName" type = "file"><br>
<input type = "submit" value = "点击上传">
</form>
后端
@RestController
public class UpFileController {
@RequestMapping(value = "/file/upload", method = RequestMethod.POST)
public void fileUpload(@RequestParam("fileName") MultipartFile file) {
//这部分都是解决文件名和文件夹的路径
String deposeFilesDir = "E:\\upload\\";
String fileName = file.getOriginalFilename();
// 如果是获取的含有路径的文件名,那么截取掉多余的,只剩下文件名和后缀名
int index = fileName.lastIndexOf("\\");
fileName = fileName.substring(index + 1);
String[] fileNameSplitArray = fileName.split("\\.");
fileName = fileNameSplitArray[0] + (int) (Math.random() * 100000) + "." + fileNameSplitArray[1];
File dest = new File(deposeFilesDir + fileName);
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdirs();
}
try{
file.transferTo(dest);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("文件的全路径名字(含路径、后缀)>>>>>>>" + deposeFilesDir + fileName);
}
}
多文件上传
前端:上传多文件的话,需在表单的input中加入multiple="multiple"。(可一次选择多个文件)
<form method="post" action="http://localhost:9876/file/mulFileUpload" enctype="multipart/form-data">
<!-- ,那么这些input按钮的name要一样-->
<input name = "fileName" multiple="multiple" type = "file"><br>
<!--如果是多个file输入控件,那么这些input按钮的name要一样-->
<!--
<input name = "fileName" type = "file"><br>
<input name = "fileName" type = "file"><br>
-->
<input type = "submit" value = "点击上传">
</form>
后端:在单文件上传的基础上,将MultipartFile 换为数组MultipartFile [],循环遍历即可。
@RequestMapping(value = "/file/mulFileUpload", method = RequestMethod.POST)
public void mulFileUpload(@RequestParam("fileName") MultipartFile[] files) {
//这部分都是解决文件名和文件夹的路径
String deposeFilesDir = "E:\\upload\\";
for (MultipartFile file : files) {
if(null != file){
String fileName = file.getOriginalFilename();
// 如果是获取的含有路径的文件名,那么截取掉多余的,只剩下文件名和后缀名
int index = fileName.lastIndexOf("\\");
fileName = fileName.substring(index + 1);
String[] fileNameSplitArray = fileName.split("\\.");
fileName = fileNameSplitArray[0] + (int) (Math.random() * 100000) + "." + fileNameSplitArray[1];
File dest = new File(deposeFilesDir + fileName);
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdirs();
}
try{
file.transferTo(dest);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("文件的全路径名字(含路径、后缀)>>>>>>>" + deposeFilesDir + fileName);
}
}
}
3、文件下载
参考网址
柔哥的:http://10.25.106.209:8090/pages/viewpage.action?pageId=17367421
其他大佬的:https://blog.csdn.net/qq_41076797/article/details/116297837
柔哥的:
文件的下载
a. 读取服务器中的文件
b. 获取Response中的字节流
c. 将读取到的文件传入获取到的字节流中
@RequestMapping("/downloadByResponse")
public void downloadByResponse(@RequestParam("accountId") String accountId, HttpServletResponse response) {
String filename = accountService.getImagePath(accountId);
if(filename != null) {
File file;
InputStream inputStream = null;
OutputStream outputStream = null;
byte[] fileByte = null;
try {
file = new File(filename);
inputStream = new FileInputStream(file);
/**
* 获取Response的字节流,并设定文件格式
**/
response.setContentType("image");
outputStream = response.getOutputStream();
int len;
byte[] bytes = new byte[1024];
while ((len = inputStream.read(bytes)) >= 0) {
outputStream.write(bytes, 0, len);
}
outputStream.flush();
} catch (Exception e) {
e.getMessage();
} finally {
try {
inputStream.close();
outputStream.close();
} catch (Exception e) {
e.getMessage();
}
}
}
}
4、layui的上传照片和查看照片
上传照片:
前端部分去看看layui官方文档的upload部分,这里只给个后端接口的写法,实际原理就是之前说过的文件上传
@PostMapping("/uploadImages")
//图片上传测试
@ResponseBody
public Response uploadImages(MultipartFile file, String productId){
String prefix = "E:\\ProList\\online-retailer\\src\\main\\resources\\static\\images\\";
//首先得到传进来的文件名称(不是ie浏览器的话一般传进来只有文件名而没有路径)
String filename = file.getOriginalFilename();
//文件后缀名从.后面一位开始
String suffix = filename.substring(filename.lastIndexOf('.'));
//总的文件路径=上级文件夹名称+文件名+后缀名
filename = prefix + productId + suffix;
//建立句柄
File localfile = new File(filename);
//如果祖辈文件夹不存在,建立上级文件夹和空文件
if (!localfile.getParentFile().exists()) {
localfile.getParentFile().mkdirs();
System.out.println("创建成功!");
}
try{
file.transferTo(localfile);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("文件的全路径名字(含路径、后缀)>>>>>>>" + filename);
response.setSuccessed("sss");
return response;
}
查看照片:
5、swagger接口文档
参考资料
实战入门:https://www.cnblogs.com/zhangweizhong/p/13185919.html
官方网站:https://swagger.io/
使用手册:https://gumutianqi1.gitbooks.io/specification-doc/content/tools-doc/spring-boot-swagger2-guide.html
Maven仓库:https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui
6、验证码的实现(这个没做,有时间来看,感谢柔哥儿)
引入依赖:
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>EasyCaptcha</artifactId>
<version>1.5.0</version>
</dependency>
将图片与内容分别存在输出流与Session中返回到前端:
public void captcha(HttpServletRequest req,HttpServletResponse res){
OutputStream outputStream = null;
try{
outputStream = res.getOutputStream();
SpecCaptcha specCaptcha = new SpecCaptcha(130,48,4);
specCaptcha.setCharType(Captcha.TYPE_DEFAULT);
String code = specCaptcha.text();
req.getSession().setAttribute("captcha",specCaptcha.text().toLowerCase());
specCaptcha.out(outputStream);
outputStream.flush();
} catch (Exception e){
e.getMessage();
} finally {
try {
outputStream.close();
} catch (Exception e){
e.getMessage();
}
}
}
接收前端验证码,与Session中存储的字符做对比:
public AccountIdResponse createAccount(@RequestBody @Validated CreateAccountRequest createAccountRequest, HttpServletRequest req) {
AccountIdResponse accountIdResponse = new AccountIdResponse();
String captcha= (String)req.getSession().getAttribute("captcha");
if(captcha.equals( createAccountRequest.getCaptcha().toLowerCase() )){
/**
* do something
**/
} else{
accountIdResponse.setStatusCode(0);
accountIdResponse.setStatusMsg("验证码错误!");
}
return accountIdResponse;
}
7、其他(下面都是转载,防止内网打不开)
常用注解:
http://10.25.106.209:8090/pages/viewpage.action?pageId=17367449
- Spring Boot
@Mapper 映射文件 Spring不主动扫描 需要在main函数中加入@MapperScan(basePackages = "com.baosight.mytutorial.persist.mapper")
@Service 提供服务 当Service由接口构成时加在具体实现类之前
@Controller @RestController 控制器
swagger
@Api(tags={接口用途}) ->放在Controller前 用于说明控制器的作用
@ApiOperation(value = "具体操作",notes = "返回内容说明") -> 放在Response或Request前 用于说明请求(响应)的具体功能
@ApiModelProperty(value = "",required = "") -> 放在Response或Request的属性前 说明属性作用Lombok
@Data 自动生成getter和setter,toString等函数其他
@NotEmpty 放在Request的String属性前 用于判断非空
@Controller 控制器 一般在请求页面的类中使用
@RestController 可以省略@ResponseBody (会自动将回复内容转化为json格式)
@RequestBody 请求体注解,一般用于请求页面(后边的变量为请求体)
@Validated 与not empty或?共同作用 判断是否有效
@Autowired 自动装配@ControllerAdvice
结合@ExceptionHandler用于全局异常的处理。用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的;
结合@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的;
结合方法型注解@ModelAttribute,表示其标注的方法将会在目标Controller方法执行之前执行。未使用的注解
@Bean
@Componnent 与@Service用途相似
@Qualifier
错误处理
http://10.25.106.209:8090/pages/viewpage.action?pageId=17367451
- mybatis将获得的数据注入到PO中
直接注入会因为下划线等原因造成Spring适应不良,因此采用ResultMap的方式将PO中的属性与数据库中的字段进行映射
将String传入到mybatis中
- 在Mapper接口的参数前加注解@Param("变量名")
- 在MyBatis文件中按照正常方式传值
空指针错误
对null对象使用圆点运算符(".")会造成空指针异常,对可能取出空值的查询,需要在调用其方法前判断一下是否非空。
若判断是否非空的是单独的对象,可以使用StringUtils.isEmpty(Bean)进行判断;
若判断的对象是ArrayList,则使用list.size()>0 进行判断。public
在接口中不需要定义访问权限,改为在实现中定义。代码规约
- 获取参数时使用@RequestBody和@RequestParam,不使用其他方式(例如,@PathVariable在传入多参数时,可能造成混乱)
- 多行注释一般用于在函数外部进行注释说明
单行注释一般用于函数间内容的说明
在文件开头需要对作者等信息进行注释,方便寻找代码责任人
注释一般单独起一行书写,不能在行尾直接书写
注释和没用的代码在项目完成后删除,以提高项目的可读性其他
可以看一下工具类的使用,比如对象
不能用==来比较,只能equals或者compareTo
比如toString有时候也要注意可能会有坑,比如给你返回一个“null”,就可以用ObjectUtils.getDisplayString
还有,和null做比较的时候,或者和一个常量做比较的时候,要把自己的变量放在比较表达式的后面,不然可能空指针异常*** 注意事项**
- 接口设计
解耦合的过程中通常将单个查询和多个查询分开实现(个人认为从Service层开始分离即可)- 事务管理
注意事务的原子性,进行事务管理的部分如果出错则全部回滚。当进行可以拆分成互不影响的小动作的方法时,可以拆分开对每个小方法单独进行事务管理。- 异常处理
异常处理除了try/catch块外,还可以duck给上一级函数进行处理,但是需要在函数定义时声明可能会抛出的异常(throws Exception),否则运行时产生的异常不会抛出,会直接导致程序报错。