FileController
package com.jt.controller;
import com.jt.service.FileService;
import com.jt.vo.ImageVO;
import com.jt.vo.SysResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
@RestController
@RequestMapping("/file")
@CrossOrigin
public class FileController {
@Autowired
private FileService fileService;
/**
* URL: /file/upload
* 类型: post
* 参数: file=字节信息
* 返回值: SysResult(ImageVO)
* 知识点:
* SpringMVC针对与IO操作开发了MultipartFile
* 底层实现就是常规IO流,简化了用户的操作过程.无需手动关流
* SpringMVC中默认支持最大1M
* 步骤:
* 1.获取文件名称
* 2.准备文件路径
* 3.准备文件上传的全路径
* 4.实现文件上传操作
*/
@PostMapping("/upload")
public SysResult upload(MultipartFile file){
ImageVO imageVO = fileService.upload(file);
if (imageVO==null){
return SysResult.fail();
}
return SysResult.success(imageVO);
}
@DeleteMapping("/deleteFile")
public SysResult deleteFile(String virtualPath){
fileService.deleteFile(virtualPath);
return SysResult.success();
}
/* 整体目的是: 将用户从前端过来的文件,解析,指定服务器存储地址,上传到服务器磁盘里*/
// 继承输入流
public SysResult upload1(MultipartFile file) throws IOException {
//将文件上传到前端,前端传给服务器处理
// 1.获取文件名称 a.jpg
String fileName = file.getOriginalFilename();
//2.准备文件目录,确保文件存在
String dir = "D:/project3/images";
File dirFile = new File(dir);//只为确保路径是否存在,保证正常存储
if(!dirFile.exists()){//判断目录是否存在
dirFile.mkdirs(); //创建多级目录
}
String path = dir + "/" + fileName;
//将文件上传到服务器磁盘,访问时先去数据库找地址,
// 根据地址访问服务器磁盘里的文件
file.transferTo(new File(path));
return SysResult.success();
}
}
FileServiceImpl
package com.jt.service;
import com.jt.vo.ImageVO;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
@Service
public class FileServiceImpl implements FileService {
//定义文件的根目录
private String rootDir = "D:/project3/images";
//定义图片服务器网址
private String rootURl = "http://image.jt.com";
/*
* 思路:
* 1.校验文件是否为图片 校验图片类型 正则表达式
* 2.防止文件为恶意程序 木马.exe.jpg 用图片属性 长和宽,属性不可赋予是自动生成,如不是图片则没有
* 3.分目录存储 按照时间维度划分
* 4.防止文件重名 UUID
*
*/
@Override
//MultipartFile类,主要是来实现以表单的形式进行文件上传功能,可获取文件的名字路径类型及转文件为输入流等
public ImageVO upload(MultipartFile file) {
/*1.获取图片名称,全部转化为小写*/
String fileName = file.getOriginalFilename().toLowerCase();
if(!fileName.matches("^.+\\.(jpg|png|gif)$")){
//java中\是转义,也是将特殊字符转普通字符,第一个\是java表示第二个\是普通的\,第二个\是正则表示后面的.是普通的.
return null;
}
/* 2.通过校验宽度和高度判断是否为图片 bufferedImage图片包装对象*/
try {
// BufferedImage可获取和操作图片基本属性 ,ImageIO可进行简单地图片IO操作,这里将file读入,生成BufferedImage对象
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
if(width == 0 || height == 0){
return null;
}
/* 3.分目录存储*/
String datePath = new SimpleDateFormat("/yyyy/MM/dd/").format(new Date());
String FileDir = rootDir + datePath;
File dirFile = new File(FileDir);
if (!dirFile.exists()){ //不能用null
dirFile.mkdirs();
}
/* 4.防止文件重名 */
String UUID = java.util.UUID.randomUUID().toString();
int index = fileName.lastIndexOf(".");
String fileType = fileName.substring(index);
fileName = UUID+fileType;
/* 5.文件上传(存到服务器磁盘)*/
String path = FileDir + fileName;
file.transferTo(new File(path));
/* 6.准备ImageVO数据返回 /2021/11/11/uuid.jpg*/
//封装vo对象
String virtualPath = datePath + fileName;
// String urlPath = "https://img14.360buyimg.com/n1/s546x546_jfs/t1/171192/11/27659/163115/61b021c9E8fbcf225/ac484bab6fac460e.jpg";
//按照要求封装图片网络地址
//http://image.jt.com/2021/11/11/uuid.jpg
String urlPath = rootURl + virtualPath;//暂时不能打开,因为服务器网址还没开
//可以在网址换成全本地全地址,能在网页打开图片
System.out.println(urlPath);
//virtualPath虚拟地址就是动态地址,url网络地址:域名+虚拟地址
ImageVO imageVO = new ImageVO(virtualPath, urlPath, fileName);
return imageVO;
} catch (IOException e) {
//一般条件下为了不影响代码结构,将检查异常,转化为运行时异常
//往外抛异常影响层级结构
//自己try-catch要是出错无法处理,
//所以转换常运行时异常
e.printStackTrace();
throw new RuntimeException(e);
}
}
@Override
public void deleteFile(String virtualPath) { //传过来会解码
String localPath = rootDir + virtualPath;
File file = new File(localPath);
if (file.exists()){
file.delete();//delete()只能删除空包或文件
}
}
}
ItemController
package com.jt.controller;
import com.jt.pojo.Item;
import com.jt.service.ItemService;
import com.jt.vo.ItemVO;
import com.jt.vo.PageResult;
import com.jt.vo.SysResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@CrossOrigin
@RequestMapping("/item")
public class ItemController {
@Autowired
private ItemService itemService;
/* 商品列表展现 */
@GetMapping("/getItemList")
public SysResult getItemList(PageResult pageResult){
pageResult = itemService.getItemList(pageResult);
return SysResult.success(pageResult);
}
/* 商品新增 */
@PostMapping("/saveItem")
public SysResult saveItem(ItemVO itemVO){
itemService.saveItem(itemVO);
return SysResult.success();
}
/* 商品删除 */
@DeleteMapping("/deleteItemById")
public SysResult deleteItemById(int id){
itemService.deleteItemById(id);
return SysResult.success();
}
/* 商品 状态 修改 */
@PutMapping("/updateItemStatus")
public SysResult updateItemStatus(Item item){
itemService.updateItemStatus(item);
return SysResult.success();
}
/* 商品数据修改 */
}
ItemServiceImpl
package com.jt.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jt.mapper.ItemDescMapper;
import com.jt.mapper.ItemMapper;
import com.jt.pojo.Item;
import com.jt.pojo.ItemDesc;
import com.jt.vo.ItemVO;
import com.jt.vo.PageResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.List;
@Service
public class ItemServiceImpl implements ItemService{
/* 一个表对应的建立一个Mapper,注入对应类的泛型
* 两个关联的表可注入同一个service,一起使用
* */
@Autowired
private ItemMapper itemMapper;
@Autowired
private ItemDescMapper itemDescMapper;
/* 商品列表展现 */
@Override
@Transactional
public PageResult getItemList(PageResult pageResult) {
//模糊查询
String query = pageResult.getQuery();
boolean flag = StringUtils.hasLength(query);
QueryWrapper<Item> queryWrapper = new QueryWrapper<>();
queryWrapper.like(flag,"title",query);//查询条件
List<Item> list = itemMapper.selectList(queryWrapper);//结果列表
//定义分页对象
IPage<Item> page =
new Page<>(pageResult.getPageNum(), pageResult.getPageSize());
//调用分页查询,传入分页对象和条件构造器
//相当于传入查询条件,分页页数,条数,底层自行根据这些帮分页,省了自己分页
//返回的还是个page对象,但此时的page有2+2信息
page = itemMapper.selectPage(page, queryWrapper);
long total = page.getTotal();//获取查询结果总条数
List<Item> records = page.getRecords();//获取传入条数和页数,数据后的分页结果
//存入pageresult返回给前面
pageResult.setTotal(total).setRows(records);
System.out.println("加载完成");
return pageResult;
}
/* 商品新增
* 需求: 完成2部分入库操作
* 步骤1: 完成Item入库操作
* 步骤2: 完成ItemDesc入库操作 item.id=itemDesc.id
* mybatis 知识讲解
* <insert id="xxxx" useGeneratedKeys="true"
* keyColumn="id"
* keyProperty="id">
* 新增sql
* </insert>
* MP知识讲解:
* MP基于对象的方式操作数据,如果实现数据的入库操作,
* 则数据都会与对象绑定,动态回显.
* 难点知识: 如何实现数据回显!!!!!!
* @param itemVO
*/
@Override
@Transactional
public void saveItem(ItemVO itemVO) {
Item item = itemVO.getItem().setStatus(true);
//刚开始id为null,入库操作时候,id在数据库中会自动赋值
//赋值之后,对象中的ID依然为null
itemMapper.insert(item);
ItemDesc itemDesc = itemVO.getItemDesc().setId(item.getId());
itemDescMapper.insert(itemDesc);
}
/* 商品删除 */
@Override
@Transactional
public void deleteItemById(int id) {
itemMapper.deleteById(id);
itemDescMapper.deleteById(id);
}
/* 商品状态修改 */
@Override
public void updateItemStatus(Item item) {
itemMapper.updateById(item);
}
}
MybatisPlusConfig
package com.jt.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*MybatisPlus可以实现跨数据库.
用户操作的是对象,但是由MP动态的生成对应的Sql语句.
如果需要使用分页,则需要额外的指定数据库版本. 需要编辑配置类.*/
@Configuration //标识配置类
public class MybatisPlusConfig {
//将自定义的对象交给Spring容器管理 告诉MP 使用的mysql/mariadb数据库
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor
(new PaginationInnerInterceptor(DbType.MARIADB));
return interceptor;
}
}
SystemException
package com.jt.aop;
import com.jt.vo.SysResult;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/* 全局异常处理机制,所有异常都会往上抛,都会经过,只拦截cController层就行, */
@RestControllerAdvice //advice 通知,
public class SystemException {
//指定异常进行拦截捕获
@ExceptionHandler(RuntimeException.class)
public SysResult exception(Exception e){
e.printStackTrace();//处理异常,在控制台输出,不然不好找错
return SysResult.fail();//给前端页面一个正向反馈
}
}