04 CRUD 菜品 + 文件上传下载

本贴最后更新于 544 天前,其中的信息可能已经时异事殊

文件上传下载

预准备

image

image

image

image

image

前端代码:

image

会动态生成一个图像元素。

代码实现

文件上传

在 resource/backend/page 下创建 demo 文件,将资料/文件上传下载页面中 upload.html 复制到这里。在 controller 下创建 CommonController 类,

此处形参 file 必须与 form data 对应的 name 一致。

在 controller 下编写:

package com.itheima.reggie.controller;

import com.itheima.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

/**
 * @author 文件上传和下载
 * @create 2023-06-27 15:42
 */
@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {

    @Value("${reggie.path}")
    private String basePath;

    @PostMapping("/upload")
    public R<String> upload(MultipartFile file){
        //file是一个临时文件,需要转存到指定位置,否则本次请求完成后临时文件会删除
        log.info(file.toString());

        //originalFilename原始文件名,suffix截取的abc.jpg的后缀jpg
        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){
            e.printStackTrace();
        }

        //因为后续需要文件名称保存到新增菜品的字段当中去,所以这里需要返回fileName
        return R.success(fileName);
    }
}

在 springboot 配置文件下指定缓存路径:

reggie:
  path: D:\img

注意:path 冒号后面必须要有空格,否则会报 Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException:​错误。

在 test 包下创建一个 UploadFileTest​类,进行单元测试,看文件路径输出是否正确:

import org.junit.jupiter.api.Test;

/**
 * @author shkstart
 * @create 2023-06-27 16:17
 */
public class UploadFileTest {
    @Test
    public void test1(){
        String fileName="adsd.jpg";
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        System.out.println(suffix);
    }
}

文件下载

image

image

先上传再发送请求下载下来,通过标签展示数据。因为后端回传了数据名称 filename,所以 response.data 的值就是这个。这里发送请求 download

image

    /**
     * 文件下载
     * @param name
     * @param response
     */
    @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){
                outputStream.write(bytes,0,len);
                outputStream.flush();
            }

            //关闭资源
            outputStream.close();
            fileInputStream.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

新增菜品

预准备

image

image

dish 表:

image

dish_flavour 表:

image

image

image

image

前端代码

image

代码实现

创建相应的 dishflavor 基本结构。

查询分类数据

image

1 代表菜品分类,2 代表套餐分类。

image

 /**
     * 查询菜品数据,用于前端下拉菜单中展示
     * @param category
     * @return
     */
    @GetMapping("/list")
    public R<List<Category>> list(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);
    }

新增菜品

image

image

image

image

image

在 reggie 下创建一个 dto 包,从资料复制 dishDto。

在 dishservice 下,新增一个 public void addWithFlavor(DishDto dishDto);​方法,在​DishServiceImpl​实现类下,编写代码:

package com.itheima.reggie.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.reggie.dto.DishDto;
import com.itheima.reggie.entity.Dish;
import com.itheima.reggie.entity.DishFlavor;
import com.itheima.reggie.mapper.DishMapper;
import com.itheima.reggie.service.DishFlavorService;
import com.itheima.reggie.service.DishService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author shkstart
 * @create 2023-06-26 21:21
 */
@Slf4j
@Service
public class DishServiceImpl extends ServiceImpl<DishMapper,Dish> implements DishService{
    @Autowired
    private DishFlavorService dishFlavorService;

    /**
     * 新增菜品,同时保存对应的口味数据
     * @param dishDto
     */
    //因为涉及到多张表操作,所以要开启事务操作
    @Transactional
    public void addWithFlavor(DishDto dishDto){
        //保存菜品的基本信息到菜品表dish
        this.save(dishDto);

        //为每个口味加上菜品id信息
        Long dishDtoId=dishDto.getId();//菜品id

        //lambda表达式+mybatisplus语法
        List<DishFlavor> flavors=dishDto.getFlavors();
        flavors=flavors.stream().map((item)->{
            item.setDishId(dishDtoId);
            return item;
        }).collect(Collectors.toList());

        //保存菜品口味数据到菜品口味表dish_flavor
        dishFlavorService.saveBatch(flavors);
    }
}

然后在启动类前加个 @EnableTransactionManagement​注解。

最后在​DishController​里面写:

    /**
     * 新增菜品,requestbody是反序列化前端传来的json数据
     * @param dishDto
     * @return
     */
    @PostMapping
    public R<String> save(@RequestBody DishDto dishDto){
        log.info(dishDto.toString());
        dishService.addWithFlavor(dishDto);
        return R.success("新增菜品成功");
    }

菜品信息分页查询

预准备

image

image

image

因为 dto 这个类只包含了菜品 id 没有包含名称,所以还要做一些额外的处理。用 Dto 来扩展出这个菜品名称。

image

展示菜品图片的前端代码:

image

image

代码实现

在 dishController 下写:

/**
     * 菜品信息分页查询
     * @param page
     * @param pageSize
     * @param name
     * @return
     */
    @GetMapping("/page")
    public R<Page> page(int page, int pageSize, String name){
        //构造分页构造对象
        Page<Dish> pageInfo=new Page<>(page,pageSize);
        Page<DishDto> dishDtoPage=new Page<>(page,pageSize);

        //条件构造器
        LambdaQueryWrapper<Dish> queryWrapper=new LambdaQueryWrapper<>();
        //添加过滤条件
        queryWrapper.like(name!=null,Dish::getName,name);
        //添加排序条件
        queryWrapper.orderByDesc(Dish::getUpdateTime);
        //执行分页查询
        dishService.page(pageInfo,queryWrapper);

        //对象拷贝,拷贝的分页信息,因为dishDto没有条件构造器,所以要先拷贝他的分页信息
        BeanUtils.copyProperties(pageInfo,dishDtoPage,"records");

        List<Dish> records = pageInfo.getRecords();
        List<DishDto> list = records.stream().map((item)->{
            DishDto dishDto=new DishDto();
            //拷贝的数据信息,并且加了一个字段存储查询id对应的菜品名称
            BeanUtils.copyProperties(item,dishDto);

            //根据id查询分类
            Long categoryId=item.getCategoryId();
            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(pageInfo);
        return R.success(dishDtoPage);
    }

修改菜品

预准备

image

image

代码实现

数据回显

DishService​下加一个 public DishDto getByIdWithFlavor(Long id);​方法,根据 id 查询菜品信息和对应的口味信息。

在​DishServiceImpl​实现类下,写:

     /**
     * 根据id查询菜品信息和对应的口味信息
     * @param id
     * @return
     */
    @Override
    public DishDto getByIdWithFlavor(Long id) {
        //查询菜品基本信息,从dish表查询
        Dish dish=this.getById(id);

        DishDto dishDto=new DishDto();
        BeanUtils.copyProperties(dish,dishDto);

        //查询当前菜品对应的口味信息,从dishFlavor表查询
        LambdaQueryWrapper<DishFlavor> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(DishFlavor::getDishId,dish.getId());
        List<DishFlavor> flavors = dishFlavorService.list(queryWrapper);
        dishDto.setFlavors(flavors);
        return dishDto;
    }

在​DishController​下,编写:

    /**
     * 根据id查询菜品信息和对应的口味信息
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public R<DishDto> get(@PathVariable Long id){
        DishDto dishDto=dishService.getByIdWithFlavor(id);
        return R.success(dishDto);
    }

修改菜品

跟新增类似。

DishController​类下,新增:

    /**
     * 修改菜品,requestbody是反序列化前端传来的json数据
     * @param dishDto
     * @return
     */
    @PutMapping
    public R<String> update(@RequestBody DishDto dishDto){
        log.info(dishDto.toString());
        dishService.updateWithFlavor(dishDto);
        return R.success("新增菜品成功");
    }

在服务类下,新增 public void updateWithFlavor(DishDto dishDto);​方法,并在实现类下重写该方法:

@Transactional
    @Override
    public void updateWithFlavor(DishDto dishDto) {
        //更新dish的基本信息
        this.updateById(dishDto);

        //清理当前菜品对应口味数据,dishflavor表的delete操作
        LambdaQueryWrapper<DishFlavor> queryWrapper=new LambdaQueryWrapper();
        queryWrapper.eq(DishFlavor::getDishId,dishDto.getId());//根据dishDto的菜品id查找dishFlavor中对应的DishId,这两是一个数值

        dishFlavorService.remove(queryWrapper);

        //添加当前提交过来的口味数据,dishflavor表的insert操作
        List<DishFlavor> flavors = dishDto.getFlavors();

        //捆绑口味和菜品id
        flavors=flavors.stream().map((item)->{
            item.setDishId(dishDto.getId());
            return item;
        }).collect(Collectors.toList());

        //保存菜品口味数据到菜品口味表dish_flavor
        dishFlavorService.saveBatch(flavors);
    }
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3190 引用 • 8214 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...