> 文档中心 > 【从零开始分析项目实战】10-菜品管理业务开发(文件的上传与下载)

【从零开始分析项目实战】10-菜品管理业务开发(文件的上传与下载)


注:本文章基于黑马程序员相关视频及资料进行编写,代码简单,较容易理解,若有问题或者源码资料获取可以在评论区留言或者联系作者!

文章目录

  • 开篇
  • 一、文件的上传与下载
      • (1)文件的上传
      • (2)文件的下载
  • 二、新增菜品
      • (1)需求分析:
      • (2)代码开发-准备工作
      • (3)梳理交互过程
      • (4)编码实现:
  • 三、菜品信息分页查询
      • (1)需求分析:
      • (2)交互过程:
      • (3)编码实现
  • 四、修改菜品
      • (1)需求分析:
      • (2)交互过程:
      • (3)编码实现:
  • 总结:

开篇

【从零开始分析项目实战】10-菜品管理业务开发(文件的上传与下载)

菜品的起售、停售自行实现

一、文件的上传与下载

(1)文件的上传

也称为upload,是指将本地图片,视频,音频等文件上传到服务器,可以供其它用户浏览或下载的过程,文件上传在项目中应用非常广泛,我们经常发微博,朋友圈等都用到了文件上传功能;

服务端接受客户端页面上传的文件,通常会使用Apache的两个组件:

  • commons-fileupload
  • commons-io

spring框架在spring-web包中对文件上传作了封装处理,大大简化了服务端代码,我们只需要在controller方法中声明一个MultipartFile类型的参数即可接受上传的文件;

(1)文件上传的初步实现:

 /*    * 文件上传*/    @PostMapping("/upload")    public R<String> upload(MultipartFile file) throws IOException {//注意这里的对象名必须和前端传过来的name相同; //file是一个临时文件,需要转存到指定位置,否则本次请求完成后,临时文件就会自动删除 log.info(file.toString()); //将临时文件转存到指定位置 file.transferTo(new File("F:\\springboot.jpg")); return null;    }

【从零开始分析项目实战】10-菜品管理业务开发(文件的上传与下载)
(3)文件上传的功能完善

    /*    * 文件上传*/    @PostMapping("/upload")    public R<String> upload(MultipartFile file) throws IOException {//注意这里的对象名必须和前端传过来的name相同; //file是一个临时文件,需要转存到指定位置,否则本次请求完成后,临时文件就会自动删除 log.info(file.toString()); //获取原始文件名 String originalFilename = file.getOriginalFilename(); String suffix=originalFilename.substring(originalFilename.lastIndexOf(".")); //创建一个目录对象 File dir=new File(basePath); //判断当前目录是否存在 if(!dir.exists()){     //如果不存在,则创建     dir.mkdirs(); } //使用uuid重新生成文件名,防止文件名重复造成文件覆盖 String filename = UUID.randomUUID().toString()+suffix;//xxxx.jpg //将临时文件转存到指定位置 file.transferTo(new File(basePath+filename)); return R.success(filename);    }

(2)文件的下载

文件下载,也称为download,是指将文件从服务器传输到本地计算机的过程。
通过浏览器进行文件下载,有两种表现形式:

  • 以附件形式下载,弹出保存对话框,将文件保存到指定磁盘目录
  • 直接在浏览器中进行打开

通过浏览器进行文件下载,本质上就是服务端将文件以流的形式写回浏览器的过程;

   /*    * 文件下载    以流的方式进行返回,所以不需要数据返回*/    @GetMapping("/download")    public void download(String name, HttpServletResponse response) throws IOException { //需要使用输入流,读取文件内容 FileInputStream fileInputStream = new FileInputStream(new File(basePath + name)); //通过输出流,将文件写会浏览器,在浏览器展示图片 ServletOutputStream outputStream = response.getOutputStream(); response.setContentType("image/jpeg"); int len=0; byte[] bytes = new byte[1024]; while ((len=fileInputStream.read(bytes))!= -1){     outputStream.write(bytes,0,len);//从第一个写,写len的长度     outputStream.flush(); } outputStream.close(); fileInputStream.close();    }

【从零开始分析项目实战】10-菜品管理业务开发(文件的上传与下载)


二、新增菜品

(1)需求分析:

后台系统可以管理菜品信息,通过新增功能来添加一个菜品,在添加菜品时需要选择当前菜品所属的菜品分类,并且需要上传菜品图片,在移动端会按照菜品分类来展示对应的菜品信息;

新增菜品,实际上就是将新增页面录入的菜品信息插入到dish表,如果添加了口味做法,还需要向dish_flsvor表插入数据,所以在添加新增菜品时,涉及到两个表dish(菜品)表和dish_falvor(菜品口味)表

(2)代码开发-准备工作

在开发代码之前,需要先将用到的类和接口基本结构创建好:

  • 实体类DishFlavor(这里不再展示)
  • Mapper接口Dishflavor
@Mapperpublic interface DishFlavorMapper extends BaseMapper<DishFlavor> {}
  • 业务层接口DishflavorService
public interface DishService extends IService<Dish> {}
  • 业务层实现类DishflavorServiceImpl
@Servicepublic class DishFlavorServiceImpl extends ServiceImpl implements DishFlavorService {}
  • 控制层DishController

(3)梳理交互过程

  1. 页面add.html发送ajax请求,请求服务端获取菜品分类数据并展示到下拉框中
  2. 页面发送请求进行图片上传,请求服务器保存图片
  3. 页面发送请求进行图片下载,将上传的图片进行回显
  4. 点击保存按钮,发送ajax请求,将菜品相关数据以json形式提交给服务端

(4)编码实现:

页面发送请求,服务器端返回菜品分类数据并展示到下拉框中;,在Categor中进行查询,由于页面会将查询的type属性值,也就是查询的是菜品还是套餐发送给后端服务器,所以用Category对象进行数据封装,然后进行查询返回;

    @GetMapping("/list")    public R<List<Category>> listR(Category category){ //条件构造器 LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>(); //添加条件 queryWrapper.eq(category.getType()!=null,Category::getType,category.getType()); //添加排序条件 queryWrapper.orderByAsc(Category::getSort).orderByDesc(Category::getUpdateTime); List<Category> list = categoryService.list(queryWrapper); return R.success(list);    }

由于前面已经对下载功能做过编码,所以这里直接进行第四步,服务器端接受前端传来的菜品信息,然后进行保存;
由于前端传来的信息具有Dish和Dishflavor两个对象,所以这里我们需要封装一个DishDto,用于封装页面提交的数据;
DTO:全称为Data TransferObject。及数据传输对象,一般用于展示层与服务层之间的数据传输
【从零开始分析项目实战】10-菜品管理业务开发(文件的上传与下载)

@Datapublic class DishDto extends Dish {    private List<DishFlavor> flavors = new ArrayList<>();    private String categoryName;    private Integer copies;}

首先在DishService中新增方法:

//新增菜品,同时插入菜品对应的口味数据,需要操作两张表,dish,dish_flavor    public void saveWithFlavor(DishDto dishDto);

然后在DishServiceImpl中对方法进行实现:

//新增菜品,同时保存对应的口味    @Override    @Transactional    public void saveWithFlavor(DishDto dishDto) { //由于DishDto继承与Dish,所以可以直接进行保存菜品到菜品表中 this.save(dishDto); Long dishId=dishDto.getId();//菜品id //菜品口味 List<DishFlavor> flavors = dishDto.getFlavors(); flavors=flavors.stream().map((item)->{     item.setDishId(dishId);     return item; }).collect(Collectors.toList()); dishFlavorService.saveBatch(flavors);    }

运行程序:

【从零开始分析项目实战】10-菜品管理业务开发(文件的上传与下载)


三、菜品信息分页查询

(1)需求分析:

系统中的菜品数据很多的时候,如果在一个页面中展示出来会显得比较乱,不便于查看,所以采用分页的方式来展示列表的数据;

图片显示要用到之前的图片下载,菜品分类信息较为复杂
【从零开始分析项目实战】10-菜品管理业务开发(文件的上传与下载)

(2)交互过程:

  1. 页面(backend/page/food/list.html)发送ajax请求,将分页查询参数page,pageSize,name提交到服务器,获取分页数据;
  2. 页面发送请求,请求服务端进行图片下载,用于页面图片展示
    开发菜品信息分页查询功能,其实就是在服务端编写代码处理前端页面的这两次请求

(3)编码实现

这里的分页查询和我们之前的分页查询基本实现方式一样,但唯一不同的是,这里进行分页查询的时候,页面显示的是菜品的分类名称,和菜品的其它信息,而在Dish表中只有菜品的分类id,要找到菜品的分类名称,也就是说还要到Category表中通过分类id查取分类名称;

具体实现可以使用DishDto类,里面刚好也封装了一个categoryName属性,首先通过page分页查询查出Dish表中的categoryid,然后将列表数据放在DishDto类中,再进行联合查询到categoryName,也放在DishDto类中,最后返回DishDto类型的分页查询对象;

@GetMapping("/page")  public R<Page> page(int page,int pageSize,String name){      Page<Dish> pageinfo=new Page<>(page,pageSize);      Page<DishDto> dishDtoPage=new Page<>();      //条件构造器      LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();      //添加过滤条件      queryWrapper.like(name!=null,Dish::getName,name);      //添加排序条件      queryWrapper.orderByDesc(Dish::getUpdateTime);      dishService.page(pageinfo, queryWrapper);      //对象拷贝,拷贝page,pageSize等属性,但是不包括records等获取到的属性      BeanUtils.copyProperties(pageinfo,dishDtoPage,"records");      //从原来的pageinfo中将一些属性值拿出来,列表形式      List<Dish> records = pageinfo.getRecords();      //      List<DishDto> list=records.stream().map((item)->{//将records中的列表数据进行一条一条的迭代   //新建一个Dishdto类,因为里面有categoryName值   DishDto dishDto = new DishDto();   //将前端列表数据传给dishDto   BeanUtils.copyProperties(item,dishDto);   //从原来的数据获取菜品分类的id   Long categoryId = item.getCategoryId();   //根据菜品分类id到分类列表中查询菜品这条数据   Category category = categoryService.getById(categoryId);   if (category!=null){//获取菜品分类名String categoryName = category.getName();//在dishDto中对categoryName属性进行赋值dishDto.setCategoryName(categoryName);   }   return dishDto;      }).collect(Collectors.toList());//转换为集合      //对dishDtoPage的Records属性进行封装      dishDtoPage.setRecords(list);      return R.success(dishDtoPage);  }

运行项目。页面能正常显示分页查询信息;
【从零开始分析项目实战】10-菜品管理业务开发(文件的上传与下载)


四、修改菜品

(1)需求分析:

在菜品管理列表页面点击修改按钮,跳转到修改菜品页面,在修改页面回显菜品相关信息并进行修改,最后点击确定按钮完成修改操作;
【从零开始分析项目实战】10-菜品管理业务开发(文件的上传与下载)

(2)交互过程:

  1. 页面发送AJAX请求,请求服务端获取分类数据,用于菜品分类下拉框中分类信息中的回显
  2. 页面发送AJAX请求,请求服务端,根据id查询当前菜品信息,用于菜品信息回显
  3. 页面发送请求,请求服务端进行图片下载,用于页面图片回显;
  4. 点击保存按钮,页面发送AJAX请求,将修改后的菜品信息以json数据格式提交 到服务端;

(3)编码实现:

首先页面发送AJAX请求,用于菜品分类下拉框中的分类信息的回显操作上面有实现过,所以这里可以直接使用;
然后页面发送AjAX请求,请求服务端,根据id查询当前菜品信息,由于设计到Dish表和Dish_flavor表的联合查询,然后对数据的组合,我们一样可以使用之前定义的DishDto类进行组合;
具体实现方法:

  1. 在DishService类中定义一个getByIdwithFlavor的方法;
//根据上传的信息查询Dish表和DishFlavor表     public DishDto getByIdWithFlavor(Long id);}
  1. 在DishServiceImpl类中对方法进行实现,分别从Dish表和Dish_Flavr表中查询数据,然后将数据重新组装到DishDto类中进行返回;
 public DishDto getByIdWithFlavor(Long id) { DishDto dishDto = new DishDto(); //1.查询菜品的基本信息 从dish表查询 Dish dish=this.getById(id); BeanUtils.copyProperties(dish,dishDto); //2、查询当前菜品对应的口味信息,从dish_flavor表查询 LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(DishFlavor::getDishId,dish.getId()); List<DishFlavor> flavors = dishFlavorService.list(queryWrapper); dishDto.setFlavors(flavors); return dishDto;    }
  1. 在DishController中编写具体接口方法:
/*  根据id查询菜品信息和对应的口味信息  要查两张表  */@GetMapping("/{id}")  public R<DishDto> get(@PathVariable Long id){    DishDto byIdWithFlavor = dishService.getByIdWithFlavor(id);    return R.success(byIdWithFlavor);}

对于页面发送请求,进行片的回显操作之前已经实现,所以也不需要再进行操作,所以最后的是将修改的信息进行提交给服务端进行保存;
具体实现步骤:

  1. 这里保存更新也涉及到两张表的操作,所以我们可以先在DishService类中新建一个updateByIdWithFlavor的方法;
 //根据上传的信息更新Dish表和DishFlavor表     public void updateWithFlavor(DishDto dishDto);
  1. 在DishServiceImpl方法中进行实现,对上传的封装好的DishDto对象数据中的Dish和DIshFlavor数据提取出来,再分别进行操作;
/*    * 更新菜品*/    @Override    @Transactional    public void updateWithFlavor(DishDto dishDto) { //更新dish表基本信息 this.updateById(dishDto); //清理当前菜品对应的口味数据--dish_flavor表的delete操作 LambdaQueryWrapper<DishFlavor> queryWrapper =new LambdaQueryWrapper<>(); queryWrapper.eq(DishFlavor::getDishId,dishDto.getId()); dishFlavorService.remove(queryWrapper); //添加当前提交过来的口味数据,--dish_flavor表的insert操作 List<DishFlavor> flavors = dishDto.getFlavors(); flavors=flavors.stream().map((item)->{     item.setDishId(dishDto.getId());     return item; }).collect(Collectors.toList()); dishFlavorService.saveBatch(flavors);    }

3.在DishController中编写具体接口方法:

/*更新菜品*/    @PutMapping    public R<String> update(@RequestBody  DishDto dishDto){ log.info(dishDto.toString()); dishService.updateWithFlavor(dishDto); return R.success("新增菜品成功");

最后运行程序,能够在正常回显和修改菜品信息;
【从零开始分析项目实战】10-菜品管理业务开发(文件的上传与下载)

总结:

涉及到两张表的联合查询时:需要分别将两个表的数据查询出来,然后再通过一个Dto类进行封装组合(具体可以使用BeanUtils工具类的copyproperties方法) 涉及到两张表的联合新增时:需要将上传的数据(Dto类)分别获取出来,然后Dto的父类表可以直接进行插入保存,而另一个类表的数据进行公共字段的设置后,再进行插入 涉及到两个表的联合更新时,与新增类似


如果感觉内容写的还不错的话,一键三连不迷路!!!!
后面将会更新更多学习内容,一起学习吧!!!!!!
在这里插入图片描述