【SpringBoot项目】一文掌握文件上传和下载【业务开发day04】

news2024/12/27 0:33:10

文章目录

    • 前言
    • 文件上传下载
        • 文件上传介绍
        • 文件下载介绍
        • 文件上传代码实现
        • 文件下载代码实现
    • 新增菜品
        • 需求分析
        • 数据模型
        • 代码开发
        • 功能测试

🌕博客x主页:己不由心王道长🌕!
🌎文章说明:SpringBoot项目-瑞吉外卖【day04】业务开发🌎
✅系列专栏:SpringBoot项目
🌴本篇内容:对黑马的瑞吉外卖项目的day04进行笔记和项目实现🌴
☕️每日一语:这个世界本来就不完美,如果我们再不接受不完美的自己,那我们要怎么活。☕️
🚩 交流社区:己不由心王道长(优质编程社区)

前言

本章是对黑马瑞吉外卖day04的一个学习和代码实现,本章的重点在于文件的上传和下载。由于上传和下载十分重要,所以这是需要务必掌握的,特别是注意上传时的要求。至于为什么上传和下载那么重要,其实我们生活中也可以体会,比如我们更改我们的微信头像,其实就是把图片传给服务端,然后再下载回显到我们的用户端。

文件上传下载

文件上传介绍

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

文件上传时,是以表单进行提交的,而文件上传时对页面的form表单有如下要求:

在这里插入图片描述
method是:post方式、enctype是:multipart/form-data,type是:file

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

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

commons-fileupload
commons-io

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

文件下载介绍

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

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

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

文件上传代码实现

在这里插入图片描述
校验部分是一个上传成功处理器,在那里会调用一个下载的controller去把刚才上传的文件下载并回显到我们的页面。

注意上面的看这个,我们去看看它。
在这里插入图片描述
上面的分析看看就好,我们现在已经知道路径和上传的条件了,开始编写对应的代码实现它:注意这里前端传的参数是file,后端接收的时候名称要一样,这是无注解参数处理

这里导入一个专门做上传下载的页面:
在这里插入图片描述
代码实现:

package com.example.controller;

/**
 * @author 不止于梦想
 * @date 2022/11/18 14:47
 */

import com.example.commons.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;

/**
 * 实现文件上传与下载的公共方法
 */
@RestController
@RequestMapping("/common")
@Slf4j
public class CommonsController {
    //注入属性文件存放上传文件所在路径
    @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("."));
        log.info(file.toString());
        //使用uuid重新生成文件名,防止文件重名覆盖。
        String fileName = UUID.randomUUID()+suffix;
        //创建一个目录对象,
        File dir =new File(basePath);
        //判断目录是否存在
        if(!dir.exists()){//如果目录不存在
            dir.mkdir();//创建一个目录
        }
        //将临时文件转到指定目录
        try {
            file.transferTo(new File(basePath+fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
        //成功返回文件名
        return R.success(fileName);
    }
}

测试:
我选择一张坤坤的图片进行上传:
在这里插入图片描述
目录在D盘的dream下,现在这个目录还没有,我们点击上传:
在这里插入图片描述
为验证结果成功,我们输出日志:
在这里插入图片描述
在这里插入图片描述

文件下载代码实现

在上面我们说到,当我们上传文件之后,把文件名字返回给前台,前台会调用下载然后回显图片。
在这里插入图片描述
方式是get,因为是读数据。参数是文件名name。

这里先写一个简单的路径,看看是不是上传以后马上就发了下载的请求然后回显:
在这里插入图片描述
当我上传之后,打点debug显示前端会自动来调用下载回显的数据,我们现在只需要关心如下下载并且回显数据即可。

@GetMapping("/download")
    public void download(String name, HttpServletResponse response) {//这里的response是因为要回显数据
        //打印访问日志信息
        log.info("name:{}", name, response.toString());
        try {
            //读图片文件,basePath是我们原本的保存位置,name也是上传时保存的名字
            FileInputStream fileInputStream = new FileInputStream(new File(basePath + name));
            //输出流,通过输出流将文件写回浏览器
            ServletOutputStream outputStream = response.getOutputStream();
            //设置想要类型
            response.setContentType("image/jpeg");
            //通过byte输出,边读边输出
            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();
        }
    }

测试:
在这里插入图片描述
成功回显到前端页面。

新增菜品

需求分析

后台系统中可以管理菜品信息,通过新增功能来添加一个新的菜品,在添加菜品时需要选择当前菜品所属的菜品分类并且需要上传菜品图片,在移动端会按照菜品分类来展示对应的菜品信息。
在这里插入图片描述
当我们点击新建菜品时,会向后台发送一个请求,如下图:
在这里插入图片描述
上面这个请求查询的是type=1的菜品分类,我们在前面的时候写过这个,type在这里表示套餐和菜品。如下图,查询type=1,即菜品种类,然后制成一个下拉列表。路径是category
在这里插入图片描述
这是第一个要写的方法。

当我们把上面的信息填好之后,需要添加图片,图片资源就是我们上面编写的文件上传下载功能,这里可以直接用,因为路径是一样的。

当我们点击保存之后,才是挑战的开始,这里并不是直接把数据存放在一个表之中,到底怎么回事,我们去瞧瞧前端代码
在这里插入图片描述
首先是flavor:口味,是一类一类的,而且我们前面也没有flavor的数据类型,怎么接收?
这里的一个难点就是,这一个数据提交得分别存入两个表中,一个是dish_flavor,一个是dish。

数据模型

我们看看数据模型:
数据模型:

新增菜品,其实就是将新增页面录入的菜品信息插入到dih表,如果添加了口味做法,还需要向dihflaVOr表插入数据.所以在新增乘品时,涉及到两个表:
dish 菜品表
dish flavo r菜品口味表
在这里插入图片描述

代码开发

我们首先先把菜品分类下拉集合编写:
菜品分类:

@GetMapping("/list")
    public R<List<Category>> list(Category category){
        log.info(category.getType().toString());
        //条件构造器
        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);
    }

测试:
在这里插入图片描述

到这里我们还没有完成任务,当我们填完表单提交以后,我们需要在后台接收数据和分别存入数据

这里要接收数据,我们得学习一个新的知识点,DTO(data transfer object),即数据传输对象,用来把不同数据进行一个组合成为一个新的对象然后接收数据的方法。
这里直接导入官方给的dto
在这里插入图片描述
这里爆红,我们上面说了口味类型,这里还没有创建,也是直接导入官方给的数据。
导入了一个entity对象,一般就需要创建其对应三层架构的,这里也不例外。这样更有层次和结构性。

我们这里看看前端代码给我们传入了什么数据:
在这里插入图片描述
这些数据类型,接收的时候得使用数据传输对象作为参数接收,而且格式是json格式;

在这里插入图片描述
路径方式:
编写简单路径,查看是否能够接收到数据
在这里插入图片描述

在这里插入图片描述
数据可以到达,这个时候就要编写代码实现了。
我们首先要考虑的是,因为这个数据涉及到多表,Mybatisplus是没有帮我们写好的,这个时候我们应该在业务层定义一个方法,通过业务层的调用分解,把数据存入不同的表中,对controller提供一个方法即可。

package com.example.controller;

import com.example.commons.R;
import com.example.dto.DishDto;
import com.example.service.DishService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 不止于梦想
 * @date 2022/11/18 19:07
 */
@RestController
@RequestMapping("/dish")
@Slf4j
public class DishController {
    @Autowired
    DishService dishService;
    @PostMapping
    public R<String> save(@RequestBody DishDto dishDto){
        log.info("dishDto:{}",dishDto);
        dishService.saveWithFlavor(dishDto);
        return R.success("添加成功");
    }
}

package com.example.service.impl;

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

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

/**
 * @author 不止于梦想
 * @date 2022/11/17 20:31
 */
@Service
public class DishServiceImpl extends ServiceImpl<DishMapper,Dish> implements DishService {
   @Autowired
    DishFlavorService dishFlavorService;
    @Override
    /**
     * 保存信息到dish表和dishflavor表
     */
    @Transactional
    public void saveWithFlavor(DishDto dishDto) {
        //把数据存入dish表中
        this.save(dishDto);
        //菜品id
        Long dishId = dishDto.getId();
        //菜品口味
        List<DishFlavor> flavors = dishDto.getFlavors();
        //方式一
//        flavors = flavors.stream().map((item) -> {
//            item.setDishId(dishId);
//            return item;
//        }).collect(Collectors.toList());
//方式二
        for (int i = 0; i < flavors.toArray().length; i++) {
            flavors.get(0).setDishId(dishId);
        }

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


这里提供了两个处理集合的方式,第一种是老师的,第二种是我自己写的。

功能测试

在这里插入图片描述

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

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

相关文章

【SRE】MySQL8的安装方式

MySQL8的安装方式Windows下载配置配置my.ini新建data文件夹初始化将数据库加入服务修改root密码Linux下载配置配置my.ini新建data文件夹初始化将数据库加入服务修改root密码Windows 下载 https://downloads.mysql.com/archives/community/ 选择MySQL8最新版本 选择上面这个 …

node和npm的安装配置使用(借鉴数篇文章避坑)

1.Error: EINVAL: invalid argument, mkdir C:\Users\lm\‪D:\nodejs\node_global 怎么解决&#xff1f; 2.环境配置中D:\Develop\nodejs\node_global\node_modules路径的疑惑&#xff1f; 之前看了很多网上的教程&#xff0c;感觉都是在互相抄&#xff0c;没有自己的东西&am…

m多载波MC-CDMA系统单用户检测方法的研究,对比EGC,MRC,ORC以及MMSE

目录 1.算法概述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法概述 传统CDMA技术在码间串扰和多址干扰等方面存在的问题使其总体性能受到限制&#xff0c;随着OFDM技术的发展&#xff0c;出现了OFDM结合CDMA的信技术&#xff0c;即多载波CDMA技术&#xf…

服务器linux下springboot项目启动、停止、重启脚本+配置jdk+配置maven+批量启动jar包脚本

部署springboot项目配置启动、停止、重启脚本 一.在Linux环境下部署springboot项目 1、把springboot项目打成jar包&#xff0c;使用maven插件实现 1.1、引入maven插件 <build><plugins><plugin><groupId>org.springframework.boot</groupId>…

【自用】Linux-CentOS7安装配置jdk1.8

一、准备工作 步骤1.创建目录 /usr/java 并进入该目录 # 进入/usr/目录 cd /usr/# 创建java目录 mkdir java# 进入java目录 cd java步骤2.下载 jdk-8u351-linux-x64.rpm 链接&#xff1a;https://pan.baidu.com/s/1IWDf70ddcy-u_mDofBklCQ?pwdxrfy 提取码&#xff1a;xrfy …

14.PyQt5应用程序主窗口QmainWindow详解

PyQt5 应用程序主窗口 对于日常见到的应用程序而言&#xff0c;许多都是基于主窗口的&#xff0c;主窗口包含了菜单栏、工具栏、状态栏和中心区域等。 QT/PyQt中提供了以QmainWindow类为核心的主窗口框架&#xff0c;它包含了众多相关的类&#xff0c;它们的继承关系如下图所…

pygame入门之环境配置

14天学习训练营导师课程&#xff1a; 李宁《Python Pygame游戏开发入门与实战》 李宁《计算机视觉OpenCV Python项目实战》1 李宁《计算机视觉OpenCV Python项目实战》2 李宁《计算机视觉OpenCV Python项目实战》3 前两节和大家一起了解了python的基础&#xff0c;今天我们就来…

互联网电商大厂库存系统设计案例讲解

1 库存扣减 多人同时买一件商品时&#xff08;假设库存充足&#xff09;&#xff0c;每个人几乎同时下单成功&#xff0c;给人一种并行感觉。但真实情况&#xff0c; 库存只是一个数值&#xff0c;无论是存在mysql数据库还是redis缓存&#xff0c;减值时都要控制顺序&#xff0…

Go 协程与Channel管道

风离不摆烂学习日志 Day2 GO 协程 结论&#xff1a; 主线程是一个物理线程&#xff0c;直接作用在cpu上。是重量级的&#xff0c;非常耗费cpu资源。 协程是从主线程开启的&#xff0c;是轻量级的线程&#xff0c;是逻辑态。对资源消耗相对小。 Golang的协程机制是重要的特点&…

OpenStreetMap:对抗谷歌帝国的共享开源地图

OpenStreetMap&#xff1a;对抗谷歌帝国的共享免费地图 讲在前面 在步入自动驾驶的学习之后&#xff0c;不可避免地我需要去了解在驾驶领域中选择的地图格式&#xff0c;而随着研究的进一步深入&#xff0c;我逐步了解到两种较为主流的自动驾驶地图格式&#xff0c;分别是Open…

[附源码]计算机毕业设计JAVA基于JSP的美妆购买网站

[附源码]计算机毕业设计JAVA基于JSP的美妆购买网站 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM m…

虚拟机的安装搭建centos7.9.2009

这里写目录标题虚拟机的安装搭建1. 虚拟机镜像版本下载 centos7.9.20092. 虚拟机 vmware 17pro&#xff08;专业版&#xff09; 版本下载及安装配置1.2.1 虚拟机 vmware 17pro下载1.2.2 虚拟机 vmware 17pro 安装1.2.3 虚拟机 vmware 17pro 配置1.2.4 安装配置centos7操作系统&…

Arthas的使用与进阶

Arthas的使用与进阶Arthas使用与进阶一、概述二、快速安装windows下安装arthasLinux下安装arthas卸载在Linux/Unix/Mac平台Windows平台三、快速入门&#xff1a;attach一个进程1、准备2、启动arthas四、快速入门&#xff1a;常用命令接触命令介绍1、dashboard仪表板2、通过Thre…

Servlet小项目 | 基于纯Servlet手写一个单表的CRUD操作

使用纯粹的Servlet完成单表【对部门的】的增删改查操作。&#xff08;B/S结构的&#xff09; 目录 一&#xff1a;设计数据库表及原型 二&#xff1a;动态实现部门列表及详情页 三&#xff1a;实现部门删除功能 四&#xff1a;实现部门新增功能 五&#xff1a;实现部门修…

NJUPT算法分析与设计期末考试2021.11.24

NJUPT算法分析与设计期末考试2021.11.24判断简答1.算法是什么&#xff1f;算法的时间复杂度是什么&#xff1f;衡量的原则&#xff0c;标准&#xff0c;工具2.分支限界法扩展活节点的方式有哪两种&#xff0c;有什么差别&#xff1f;3.回溯法搜索子集树&#xff0c;排列树的算法…

PostgreSQL下载和安装教程

PostgreSQL下载和安装嘚吧嘚下载安装配置pgAdmin设置中文安装中遇到的问题The database cluster initialisation failed.问题描述解决方法嘚吧嘚 公司在用PostgreSQL数据库&#xff0c;和MySQL一样是免费试用的。虽然不知道公司出于什么考虑没有选择MySQL&#xff0c;而是选用…

【户外】东莞-银瓶山-常规路线-登山游记+攻略

不想看流水的请直接看最后攻略 此次路线&#xff1a;北上南下。累计行走约11.57km. 2022/11/20 周日 东莞最近YQ严重&#xff0c;不是拉去Jiankang驿站&#xff0c;就是居家GeLi&#xff0c;加上工作也郁闷&#xff0c;出去走走。 昨晚两点睡觉&#xff0c;在给各种设备补电量…

qq录屏快捷键是什么?qq录屏声音设置

我们日常生活中&#xff0c;有时会突然遇到需要用电脑录制屏幕的情况&#xff0c;这个时候我们可以通过按下qq录屏的快捷键进行录屏。有些小伙伴就有疑问了&#xff0c;电脑qq录屏快捷键是什么&#xff1f; qq录屏声音如何设置&#xff1f;别急&#xff0c;接下来小编给大家详细…

SpringMVC跳转

转发&#xff1a; 1&#xff1a;添加成功跳转到成功页面&#xff0c;给出提示&#xff0c;失败跳转到失败页面 ---- jsp 2&#xff1a;添加成功后&#xff0c;跳转到查询的controller中 -- 另外一个程序&#xff0c;添加完成之后&#xff0c;执行查询所有的操作&#xff0c…

Linux环境基础开发工具使用

Linux环境基础开发工具使用 文章目录Linux环境基础开发工具使用1.Linux软件包管理器 yum1.1 什么是软件包1.2 了解rzsz(文件传输工具)1.3 查看软件包1.4 安装与卸载软件指令1.5 更新yum源2.Linux开发工具介绍2.1 vi/vim开发工具介绍2.2 vi/vim的按键图解3.Linux编辑器---vim的使…