菜品图片上传下载
新增菜品难点主要涉及一个图片的上传下载,回显。
因为没有进行真正的云服务器操作,所以进行的上传下载只是将一个文件以流的形式转移到另一个文件夹。
并且将图片名称存进数据库中,下载时在文件夹进行查找。
- 前端上传的简单实现
要注意几点(1)要用post方式(2)以mutipart格式上传(3)以file控件上传
<form method = "post" action="/common/upload" enctype="multipart/form-data">
<input name = "myFile" type="file"/>
</form>
- 文件的上传方法实现
在spring中给了一个MultipartFile工具类进行上传操作,所以在上传方法接收一个MultipartFile的对象。
并且这个MultipartFile是一个临时的文件,如果不进行转存,那么在tomcat关闭后会销毁。
为了避免得到的图片文件重名的情况,我们需要对文件进行名字的更改,思路是得到文件名字后将后缀与文件分割,把名字通过UUID随机生成。
在重命名后,通过MultipartFile的transferTo方法可以将文件转移到一个指定路径下。
最后将文件命回传给前端。前端接收到后再通过下载方法进行回显。
需要在yml中配置一个base目录用来存放路径避免耦合度过高。
reggie:
path: 路径
使用getOriginalFilename() 方法得到文件名称,是带后缀的,再使用substring进行字符串操作得到后缀文件名称。再使用UUID得到一个随机名称加上之前获得的后缀。
String fileName = file.getOriginalFilename();
String subFileName = fileName.substring(fileName.lastIndexOf("."));
String UUIDFileName = UUID.randomUUID().toString()+subFileName;
在得到名称后,我们需要通过transferTo() 方法将文件转存到指定目录。这个方法需要一个file对象,所以新增一个file对象。此时需要增加判断,如果目录不存在那么会报错,所以判断一下,如果不存在就新增目录。
File dir = new File(basePath);
if(!dir.exists()){
dir.mkdir();
}
try {
file.transferTo(new File(basePath + UUIDFileName));
} catch (Exception e) {
e.printStackTrace();
}
完整方法代码如下
@PostMapping("/upload")
public R<String> upload(MultipartFile file){
String fileName = file.getOriginalFilename();
String subFileName = fileName.substring(fileName.lastIndexOf("."));
String UUIDFileName = UUID.randomUUID().toString()+subFileName;
File dir = new File(basePath);
if(!dir.exists()){
dir.mkdir();
}
try {
file.transferTo(new File(basePath + UUIDFileName));
} catch (Exception e) {
e.printStackTrace();
}
return R.success(UUIDFileName);
}
- 文件下载方法实现
方法需要传入图片名称,通过图片名称在base目录中找到图片。
文件下载主要涉及两个类,一个是文件输入流FileInputStream,用来将项目文件转成流文件。
另一个是ServletOutputStream,将文件输入流通过response响应出去,设置response的ContentType为图片形式。
FileInputStream fileInputStream = new FileInputStream(new File(basePath+name));
ServletOutputStream servletOutputStream = response.getOutputStream();
response.setContentType("image/jpeg");
下面要将文件流读入ServletOutputStream,read方法在读到末尾时会返回-1
int len = 0;
byte[] bytes = new byte[1024];
while((len=fileInputStream.read(bytes))!=-1){
servletOutputStream.write(bytes,0,len);
servletOutputStream.flush();
}
最后将流关闭
servletOutputStream.close();
fileInputStream.close();
完整代码如下
@GetMapping("/download")
public void downLoad(String name, HttpServletResponse response){
try {
response.setCharacterEncoding("utf-8");
FileInputStream fileInputStream = new FileInputStream(new File(basePath+name));
ServletOutputStream servletOutputStream = response.getOutputStream();
response.setContentType("image/jpeg");
int len = 0;
byte[] bytes = new byte[1024];
while((len=fileInputStream.read(bytes))!=-1){
servletOutputStream.write(bytes,0,len);
servletOutputStream.flush();
}
servletOutputStream.close();
fileInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
菜品新增
在菜品新增中,提出一个之前没有听说的概念就是Dto,在新增菜品的时候,除了要输入基本菜品信息还需要输入菜品口味,但是在实体类中并不存在菜品口味,菜品口味是另一个实体类,此时就不能直接按照之前的新增操作新增。
之前的新增操作:
DishService.save(dish);
return R.success("新增成功");
但是现在这样操作就会漏增加菜品口味。所以新增一个Dto类继承自Dish,这样Dish里面有的属性,我们也有,还可以根据需要增加一些特定属性。
public class DishDto extends Dish {
private List<DishFlavor> flavors = new ArrayList<>();//口味
private String categoryName;//菜品名
}
这时候,我们在Controller方法中传入的是一个Dto类。我们需要在Service中新增一个方法用来保存Dto到两个表中。
首先调用save方法保存dish类中有的部分。因为在dishService的实现类中进行这个操作,所以直接使用this就可以引用了,不要再去装配一个Service,不然会报错,因为spring不知道该初始化哪个类先。不要问我怎么知道的。
this.save(dishDto);
此时dish表中新增的菜品需要的已经保存了,但是菜品口味还没有保存,在菜品口味中除了菜品的口味数组,还有菜品id,不然就不知道口味是属于谁的。
List<DishFlavor> flavors = dishDto.getFlavors();//从传入的dishDto中获得口味
//通过stream将每个菜品的id设置
flavors.stream().map((item)->{
item.setDishId(dishId);
return item;
}).collect(Collectors.toList());
//保存到dishFlavor表中
dishFlavorService.saveBatch(flavors);
分页展示
与之前的问题一样,分页不能只是简单展示dish表中数据,因为还有一个菜品的分类类别,类别表是另一个表,两个表通过一个类别id进行关联,所以查询时,也需要传入dto对象,并将dto中的菜品名称根据id查出保存。
以下是完整代码
@GetMapping("/page")
public R<Page> Page(int page, int pageSize,String name){
//分页对象
Page<Dish> pageInfo = new Page(page,pageSize);
Page<DishDto> pageDto = new Page();
//查询
LambdaQueryWrapper<Dish> dishQueryWrapper= new LambdaQueryWrapper<>();
dishQueryWrapper.like(name!=null,Dish::getName,name);
dishQueryWrapper.orderByDesc(Dish::getUpdateTime);
//执行分页查询
dishService.page(pageInfo,dishQueryWrapper);
//把pageInfo中的值除了口味之外传到pageDto中
BeanUtils.copyProperties(pageInfo,pageDto,"records");
List<Dish> records = pageInfo.getRecords();
List<DishDto> dishDtos = records.stream().map(item->{
//Dto对象得到dish口味的其他信息
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(item,dishDto);
//获得每个的菜品名
Long categoryId = item.getCategoryId();
Category categoryUseName = categoryService.getById(categoryId);
if(categoryUseName!=null){
String categoryName = categoryUseName.getName();
dishDto.setCategoryName(categoryName);
}
return dishDto;
}).collect(Collectors.toList());
pageDto.setRecords(dishDtos);
return R.success(pageDto);
}