菜品管理业务开发文件上传与下载文件上传

介绍

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

1.commons-file upload

2.commons-io

Spring框架在spring-web包中对文件上传进行了封装。只需要在Controller的方法中声明一个MultipartFile类型的参数即可接受上传的文件

代码实现

@Value("${reggie.path}")    private String basePath;    @PostMapping("/upload")    public R upload(MultipartFile file){        //file是一个临时文件,需要把他转存到指定文件,否则本次请求完成后会被删除        log.info("上传的文件{}",file.toString());        //原始文件名        String originalFilename = file.getOriginalFilename();        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));        //使用uuid重新生成文件名,防止文件名重复造成文件被覆盖        String fileName = UUID.randomUUID().toString()+suffix;        //创建一个目录对象        File dir = new File(basePath);        //判断当前目录是否存在        if (!dir.exists()){            //目录不存在  创建目录            dir.mkdirs();        }        try {            file.transferTo(new File(basePath+fileName));        } catch (IOException e) {            throw new RuntimeException(e);        }        return R.success(fileName);    }

upload方法里的形参名要保证与前端提交的表单名一致

在yml配置文件中定义路径变量path

文件下载

代码实现

 @GetMapping("/download")    public void download(String name, HttpServletResponse response){        try {            //输入流  通过输入流读取文件内容            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){//len:m每次读取的有效字节数当len=-1表示读取到文件末尾                outputStream.write(bytes,0,len);//**写出指定长度字节数组**:`write(byte[] b, int off, int len)` ,每次写出从off索引开始,len个字节,                outputStream.flush();            }            outputStream.close();            fileInputStream.close();        } catch (FileNotFoundException e) {            throw new RuntimeException(e);        } catch (IOException e) {            throw new RuntimeException(e);        }    }

新增菜品功能需求分析

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

数据模型

代码开发

准备工作:

实体类DishFlavor

Mapper接口DishFlavorMapper

业务层接口DishFlavorService

业务层实现类 DishFlavorServicelmpl控制层 DishController

交互过程:

1、页面(backend/page/food/ add.html)发送ajax请求,请求服务端获取菜品分类数据并展示到下拉框中

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

2、页面发送请求进行图片上传,请求服务端将图片保存到服务器

3、页面发送请求进行图片下载,将上传的图片进行回显

4、点击保存按钮,发送ajax请求,将菜品相关数据以json形式提交到服务端

前端提交的json数据与dish实体类不是一一对应,需要定义dishDao类来接受数据

DTO:全称为Data Transfer Object,即数据传输对象,一般用于展示层和服务层之间的数据传输

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

菜品信息分页查询代码开发-梳理交互

1、页面(backend/page/food/list.html)发送ajax请求,将分页查询参数(page、pageSize,name)
提交到服务端,获取分页数据
2、页面发送请求,请求服务端进行图片下载,用于页面图片展示

代码实现

/*** 菜品分页查询* @param page* @param pageSize* @param name* @return*/@GetMapping("/page")public R page(int page,int pageSize,String name){//构造分页构造器对象Page pageInfo = new Page(page,pageSize);Page dishDtoPage = new Page();//构造查询构造器对象LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();queryWrapper.like(name!=null,Dish::getName,name);queryWrapper.orderByDesc(Dish::getUpdateTime);dishService.page(pageInfo,queryWrapper);//对象拷贝BeanUtils.copyProperties(pageInfo,dishDtoPage,"records");List records = pageInfo.getRecords();List list= records.stream().map((item)->{DishDto dishDto = new DishDto();BeanUtils.copyProperties(item,dishDto);Long categoryId = item.getCategoryId();//根据id查询分类对象Category category=categoryService.getById(categoryId);if (category!=null){String categoryName = category.getName();dishDto.setCategoryName(categoryName);}return dishDto;}).collect(Collectors.toList());dishDtoPage.setRecords(list);return R.success(dishDtoPage);}

前端渲染页面需要categoryName参数。实体类dish中无对应属性,所以不能直接将获取到的pageInfo响应给客户端。再声明泛型为DishDao的Page对象。将pageInfo中的属性拷贝到dishDaoPage中返回给客户端,但由于pageInfo中的records属性中寸的是dish对象,需要单独处理,将records中的dish对象转换为dishDao对象存到新集合中。最后将dishDaoPage返回给前端。

修改菜品需求分析

在菜品管理列表页面点击修改按钮,跳转到修改菜品页面,在修改页面回显菜品相关信息并进行修改,点击确定按钮完成相关操作

代码实现

页面的回显信息中包含菜品口味,因此服务端应向客户端返回dishDto类型的数据,在dishService中声明getByIdWithFlavor方法,根据id查询菜品信息以及菜品口味信息封装成dishDto对象,返回

  @Override    public DishDto getByIdWithFlavor(Long id) {        Dish dish = this.getById(id);        DishDto dishDto = new DishDto();        BeanUtils.copyProperties(dish,dishDto);        //查询当前菜品的口味信息        LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();        queryWrapper.eq(DishFlavor::getDishId,dish.getId());        List list = dishFlavorService.list(queryWrapper);        dishDto.setFlavors(list);        return dishDto;    }

调用dishSer中的getByIdWithFlavor方法,将返回值响应给客户端,实现数据回显

 @GetMapping("/{id}")    public R get(@PathVariable Long id){        DishDto byIdWithFlavor = dishService.getByIdWithFlavor(id);        return R.success(byIdWithFlavor);    }

将修改后的数据保存到数据库中,需要分别更新菜品表和菜品口味表。

菜品口味表的更新分两步,第一步先将当前口味表中对应的数据删除,再添加客户端提交过来的数据实现数据更新

  @Override    public void updateWithFlavor(DishDto dishDto) {        //更新dish表基本信息        this.updateById(dishDto);        //清理当前菜品口味对应数据-----dishFlavor表对应的删除操作        LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();        queryWrapper.eq(DishFlavor::getDishId,dishDto.getId());        dishFlavorService.remove(queryWrapper);        //添加提交过来的口味数据        List flavors = dishDto.getFlavors();        flavors = flavors.stream().map((item)->{            item.setDishId(dishDto.getId());            return item;        }).collect(Collectors.toList());        dishFlavorService.saveBatch(flavors);    }
 /**     * 修改菜品     * @param dishDto     * @return     */    @PutMapping    public R update(@RequestBody DishDto dishDto){        log.info(dishDto.toString());        dishService.updateWithFlavor(dishDto);        return R.success("修改菜品成功");    }