springboot项目外卖管理 day04-文件的上传下载与菜品的新增与修改

news2024/11/30 13:38:10

文章目录

  • 1、文件上传下载
    • 1.1、文件上传介绍与实现
    • 1.2、文件下载介绍与实现
  • 2、新增菜品
    • 2.1、需求分析
    • 2.2、代码开发-梳理交互过程
      • 2.2.1、菜品分类下拉框:在CategoryController添加
    • 2.2.2、新增保存
  • 3、菜品信息分页查询
    • 3.1、代码开发-梳理交互过程
      • 对象拷贝BeanUtils.copyProperties
  • 4、修改菜品
    • 4.1、需求分析
    • 4.2、代码开发-梳理交互过程
    • 4.3、页面回显数据实现
      • 4.3.1、DishController处理Get请求
      • 4.2.2、在DishServiceImpl添加getByIdWithFlavor方法
    • 4.4、修改菜品实现
  • 总结


1、文件上传下载

1.1、文件上传介绍与实现

文件上传,也称为upload,是指将本地图片、视频、音频等文件上传到服务器上,可以供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛,我们经常发微博、发微信朋友圈都用到了文件上传功能。
文件上传时,对页面的form表单有如下要求:
method=“post”            采用post方式提交数据
enctype=“multipart/form-data”     采用multipart格式上传文件
type=“file”              使用input的file控件上传

在这里插入图片描述
目前一些前端组件库也提供了相应的上传组件,但是底层原理还是基于form表单的文件上传。例如ElementUI中提供的upload上传组件:

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

Spring框架在spring-web包中对文件上传进行了封装,大大简化了服务端代码,我们只需要在Controller的方法中声明一个MultipartFile类型的参数即可接收上传的文件。

代码实现

@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {
    @Value("${reggie.path}")
    private String basePath;

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

        //原始文件名
        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();
        }
        return R.success(fileName);
    }
}

1.2、文件下载介绍与实现

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

1.以附件形式下载,弹出保存对话框,将文件保存到指定磁盘目录
2.直接在浏览器中打开
通过浏览器进行文件下载,本质上就是服务端将文件以流的形式写回浏览器的过程。

文件下载代码实现

//文件下载
@GetMapping("/download")
public void download(String name, HttpServletResponse response){
    try {
        //输入流,通过输入流读取文件内容
        FileInputStream fileInputStream=new FileInputStream(new File(basePath+name));
        //输出流,通过输出流将文件写回浏览器,在浏览器中展示图片
        ServletOutputStream outputStream = response.getOutputStream();

        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();
    }
}

2、新增菜品

2.1、需求分析

后台系统中可以管理菜品信息,通过新增功能来添加一个新的菜品,在添加菜品时需要选择当前菜品所属的菜品分类,并且需要上传菜品图片,在移动端会按照菜品分类来展示对应的菜品信息。
在这里插入图片描述
新增菜品,其实就是将新增页面录入的菜品信息插入到dish表,如果添加了口味做法,还需要向dish_flavor表插入数据。所以在新增菜品时,涉及到两个表:dish(菜品表)与dish_flavor(菜品口味表)

2.2、代码开发-梳理交互过程

在开发代码之前,需要梳理一下新增菜品时前端页面和服务端的交互过程:
1、页面(backend/page/food/add.html)发送ajax请求,请求服务端获取菜品分类数据并展示到下拉框中
2、页面发送请求进行图片上传,请求服务端将图片保存到服务器
3、页面发送请求进行图片下载,将上传的图片进行回显
4、点击保存按钮,发送ajax请求,将菜品相关数据以json形式提交到服务端
开发新增菜品功能,其实就是在服务端编写代码去处理前端页面发送的这4次请求即可。

2.2.1、菜品分类下拉框:在CategoryController添加

//根据条件查询分类数据
@GetMapping("/list")
public R<List<Category>> list(Category category){
    //条件构造器
    LambdaQueryWrapper<Category> lambdaQueryWrapper=new LambdaQueryWrapper<>();
    //添加条件
    lambdaQueryWrapper.eq(category.getType()!=null,Category::getType,category.getType());
    //添加排序条件
    lambdaQueryWrapper.orderByAsc(Category::getSort).orderByAsc(Category::getUpdateTime);
    List<Category> list = categoryService.list(lambdaQueryWrapper);
    return R.success(list);
}

2和3已经实现

2.2.2、新增保存

在这里插入图片描述
通过提交的参数可以看到传入的数据不仅有dish表中内容,还有口味内容,这就不是对一张表的操作,我们可以通过mybatisi进行多表操作,也可以新建一个dto,什么是dto
注意:DTO,全称为Data Transfer object,即数据传输对象,一般用于展示层与服务层之间的数据传输。

@Data
public class DishDto extends Dish {

    private List<DishFlavor> flavors = new ArrayList<>();

    private String categoryName;

    private Integer copies;
}

同时我们也需要在DishService接口中自定义方法来实现
我们不能简单的使用dishFlavorService.saveBatch(dishDto.getFlavors());
我们知道菜品的id是基于雪花算法自动生成的,所以我们需要将菜品基本信息保存到数据库中之后,再获取菜品id将其赋值给dish_flavors中的dish_id
通过stream()可以实现,但是目前我还没有了解,于是我通过for循环遍历的方式来赋值

  for (int i=0;i<flavors.size();i++){
            flavors.get(i).setDishId(dishId);
        }
@Service
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
    @Autowired
    private DishFlavorService dishFlavorService;

    @Override
    @Transactional
    public void saveWithFlavor(DishDto dishDto) {
        //保存菜品基本信息到菜品表dish
        this.save(dishDto);

        Long dishid = dishDto.getId();
        //菜品口味
        List<DishFlavor> flavors = dishDto.getFlavors();
        //for循环方式
          for (int i=0;i<flavors.size();i++){
            flavors.get(i).setDishId(dishId);
        }
        //stream流方式
        flavors = flavors.stream().map((item) -> {
            item.setDishId(dishid);
            return item;
        }).collect(Collectors.toList());
        //dishFlavorService.saveBatch(dishDto.getFlavors());
        //保存菜品口味到菜品数据表dish_flavor
        dishFlavorService.saveBatch(flavors);
    }
}

由于涉及到了多表于是加上事务注解 @Transactional

3、菜品信息分页查询

3.1、代码开发-梳理交互过程

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

注意
这里的分页和之前的又有区别,这次页面需要的参数还有菜品分类,这也不是一张Dish能够解决的,也需要用到DishDto
还使用到了对象拷贝使用

对象拷贝BeanUtils.copyProperties

我们如果有两个具有很多相同属性的JavaBean对象a和b,想把a中的属性赋值到b
传统方式需要很多的set,get方法,但是通过BeanUtils.copyProperties(a,b)便可把对象a与b中同名属性赋上值

@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(!StringUtils.isEmpty(name), Dish::getName, name);

  //添加排序条件
  queryWrapper.orderByDesc(Dish::getUpdateTime);

  //进行分页查询
  dishService.page(pageInfo, queryWrapper);

  //对象拷贝
  BeanUtils.copyProperties(pageInfo,dishDtoPage,"records");

  List<Dish> records = pageInfo.getRecords();
  List<DishDto> 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);
}

4、修改菜品

4.1、需求分析

在菜品管理列表页面点击修改按钮,跳转到修改菜品页面,在修改页面回显菜品相关信息并进行修改,最后点击确定按钮完成修改操作
在这里插入图片描述

4.2、代码开发-梳理交互过程

在开发代码之前,需要梳理一下修改菜品时前端页面( add.html)和服务端的交互过程:
1、页面发送ajax请求,请求服务端获取分类数据,用于菜品分类下拉框中数据展示
2、页面发送ajax请求,请求服务端,根据id查询当前菜品信息,用于菜品信息回显

4.3、页面回显数据实现

4.3.1、DishController处理Get请求

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

4.2.2、在DishServiceImpl添加getByIdWithFlavor方法

@Override
@Transactional
public DishDto getByIdWithFlavor(Long id) {
    //查询菜品基本信息
    Dish dish = this.getById(id);

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

    //查询菜品口味信息
    LambdaQueryWrapper<DishFlavor> queryWrapper=new LambdaQueryWrapper<>();
    queryWrapper.eq(DishFlavor::getDishId,dish.getId());
    List<DishFlavor> list = dishFlavorService.list(queryWrapper);

    dishDto.setFlavors(list);

    return dishDto;
}

4.4、修改菜品实现

在做之前首先自己先尝试做了一下,思路主要是遍历disDto中的Flavor数据,判断其中id是否为空如果为空则表示,为新增的口味,需要加上菜品id执行新增操作,否则直接执行更新操作,这样就会出现一个问题,如果前端减少了口味需求,那我们后端也无从得知,数据库便不会变化

@Override
    public void updateWithFlavor(DishDto dishDto) {
        Dish dish=new Dish();
        BeanUtils.copyProperties(dishDto,dish);
        dishService.updateById(dish);
        Long id=dish.getId();
        //菜品口味
        List<DishFlavor> flavors = dishDto.getFlavors();
        for (int i=0;i<flavors.size();i++){
                if(flavors.get(i).getId()!=null)
                {
                 dishFlavorService.updateById(flavors.get(i));
                }
                else {
                    flavors.get(i).setDishId(id);
                    dishFlavorService.save(flavors.get(i));
                }
            }
        }

后面学习了新思路 就是先把口味数据都删掉,把前端传来的数据直接当新数据插入,这样就解决了不能删除的问题,同时代码也更简洁

@Override
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);
}

总结

day04主要学到的有
1、文件的上传下载,与设置文件的保存路径
2、当前端返回的数据不止一张表中字段时可以新建dto类再进行对应操作
3、学会了BeanUtils工具类中的copyProperties函数 进行对象的复制
4、学习了一个新思路,可以把修改改成先删除再插入

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/625795.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:Python输出方法详解:从基础print()到高级logging模块

Python输出方法详解&#xff1a;从基础print()到高级logging模块 在Python编程中&#xff0c;输出是一个必不可少的步骤。然而&#xff0c;Python提供了多种输出方法&#xff0c;如何选择最适合的方法呢&#xff1f;本文将详细介绍Python输出的不同方法&#xff0c;并给出实际…

只给大模型LeetCode编号,也能解题!大模型表现好是源于对训练数据的记忆吗?请不要迷信大模型

夕小瑶科技说 原创 作者 | Python 自从推出以来&#xff0c;ChatGPT这款智能高效的人机对话平台迅速风靡全球。人们开始广泛尝试使用ChatGPT来解决各种问题&#xff0c;无论是医学检测报告的解释&#xff0c;还是公众号文章的取名&#xff0c;甚至是论文修改润色和rebuttal撰…

微服务治理框架- - -Spring Cloud

前言&#xff1a;最近微服务很是火热&#xff0c;那么什么是微服务&#xff1f;相信小伙伴们对此也是一知半解&#xff0c;那么今天叶秋学长带领大家一起学习微服务治理框架Spring Cloud&#xff0c;快来跟着学长一起学习吧~~ 目录 对SpringCloud了解多少&#xff1f; 什么是…

【动态规划】NK刷题之DP7 连续子数组的最大乘积

【动态规划】NK刷题之DP7 连续子数组的最大乘积 1.题目2.题解3.代码部分法一&#xff1a;动态规划3.1.1 创建变量n&#xff0c;并读入数据3.1.2 创建动态数组&#xff0c;并初始化3.1.3 对动态数组断言3.1.4 读入原整形数组的数据3.1.5 创建变量ret&#xff0c;并赋初值3.1.6 循…

chatgpt赋能python:Python怎么下jieba库

Python怎么下jieba库 Python是目前最流行的动态编程语言之一&#xff0c;广泛应用于Web开发、数据分析、人工智能等领域。对于中文文本处理来说&#xff0c;jieba库是一款非常实用的工具。本文将介绍如何下载jieba库&#xff0c;并探讨其在中文分词、情感分析等方面的应用。 …

week7 表示学习(Representation Learning) Part1--Pretext Text

文章目录 Representation LearningInferring structure&#xff08;推断结构&#xff09; Transformation predictionRotation predictionRelative transformation prediction ReconstructionDenoising AutoencodersContext encodersColorizationSplit-brain encoders Instance…

Keras-3-实例3-回归问题

1. 回归问题 1.1 波士顿房价数据集加载: 预测20世界70年代中期波士顿郊区房屋价格的中位数。 已知当时郊区有一些数据点&#xff0c;如犯罪率、房产税率等。 与IMDB和路透社数据集相比&#xff0c;波士顿房价数据集样本量比较少&#xff0c;只有506个样本&#xff1b;同时&a…

数据结构——绪论/线性表

文章目录 **一 基本概念****二 算法和算法评价****三 线性表的定义和基本操作****四 线性表的顺序表示****1 定义****2 基本操作** **五 线性表的链式表示****1 单链表的定义****2 单链表的基本操作实现****3 双链表****4 循环链表****5 静态链表** 一 基本概念 数据类型&…

使用pycharm入门python的一些注意点

今儿在帮别人跑一段python代码&#xff0c;实际上我对python并不熟悉&#xff0c;只能边摸索边尝试。选择了pycharm这个工具。 一.怎么安装python使用的库文件 能用来安装python的库文件的&#xff0c;有很多种办法&#xff0c;这里只介绍pip和pip3。因为pip和pip3的优势是能…

SpringCloud第一篇:服务的注册与发现Eureka

一、spring cloud简介 spring cloud 为开发人员提供了快速构建分布式系统的一些工具&#xff0c;包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。它运行环境简单&#xff0c;可以在开发人员的电脑上跑。 二、创建服务注册中心 …

oracle细节、经验锦集

【版权声明】未经博主同意&#xff0c;谢绝转载&#xff01;&#xff08;请尊重原创&#xff0c;博主保留追究权&#xff09; https://blog.csdn.net/m0_69908381/article/details/131054454 出自【进步*于辰的博客】> 由于是随兴记录&#xff0c;因此阐述的比较多而乱&…

详解Eureka服务注册和调用

目录 1.概述 2.环境 2.1.项目结构 2.2.项目架构 2.3.依赖 3.注册中心 3.1.配置使用 3.2.管理页 3.3.集群 4.服务注册 5.服务调用 5.2.Ribbon 5.3.Feign 5.4.OpenFeign 1.概述 我们把从外部进入到微服务集群里的流量称为“南北流量”、微服务集群节点间流转的流量…

HTML type=“radio“ 不显示按钮

问题 HTML中type&#xff1d;"radio" 但是在界面中不显示按钮。 详细问题 HTML中type&#xff1d;"radio" 但是在界面中不显示按钮。 笔者html核心代码 <div>性别<input type"radio" id"male" name"gender" va…

chatgpt赋能python:Python中的中文分词神器——jieba

Python中的中文分词神器——jieba 介绍 如果你曾经在处理中文文本时&#xff0c;也许会遇到中文分词的需求&#xff0c;jieba就是一款不可错过的工具。 jieba 是目前最好的 Python 中文分词库&#xff0c;它具有高效、简单和可定制等优点&#xff0c;适合各种规模的文本分词…

Linux学习之vim四种模式初探

vim是一个多模式&#xff0c;有vim /所在目录/文件名&#xff0c;若是已经有了文件&#xff0c;可以打开文件进行编辑&#xff0c;若是没有文件&#xff0c;在vim中保存之后就会有一个文件&#xff0c;相当于记事本里边新建一个文件。我现在就使用vim新建一个文件&#xff0c;然…

C# .NET EF框架 webapi 安装使用sqlite

文章目录 Sqlite安装使用Sqlite特点环境安装EF 是什么&#xff1f;EF使用举例 Nuget控制台 Sqlite安装使用 Sqlite特点 Sqlite是轻量级数据库&#xff0c;不需要安装&#xff0c;跨平台使用。是用来做小软件&#xff0c;小项目&#xff0c;快速移植的最优解。不需要像mysql和…

Verilog基础:表达式中的整数常量(integer)

相关文章 Verilog基础&#xff1a;表达式位宽的确定&#xff08;位宽拓展&#xff09; Verilog基础&#xff1a;表达式符号的确定 Verilog基础&#xff1a;数据类型 Verilog基础&#xff1a;位宽拓展和有符号数运算的联系 Verilog基础&#xff1a;case、casex、ca…

chatgpt赋能python:Python如何Input一个字典:详细教程

Python 如何 Input 一个字典&#xff1a;详细教程 在 Python 中&#xff0c;字典是一种非常有用的数据结构。它可以存储键值对&#xff0c;让我们能够通过键访问值。Python 中没有固定的语法来创建字典&#xff0c;因此本文将教你如何输入一个字典。 什么是字典&#xff1f; …

chatgpt赋能python:Python语言中的Pandas库介绍

Python语言中的Pandas库介绍 Python语言是一种十分流行的编程语言&#xff0c;以其易读易写等特点而受到工程师和数据科学家的青睐。在Python语言中&#xff0c;有一款非常流行的数据处理和分析库&#xff0c;也就是Pandas库。Pandas库在数据处理和分析方面有着出色的表现&…

iOS开发最佳实践|集成声网SDK实现语音聊天室

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c; 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。&#x1f60a; 座右铭&#xff1a;不想当开发的测试&#xff0c;不是一个好…