瑞吉外卖 - 文件上传与下载功能(15)

news2025/1/1 22:40:01

某马瑞吉外卖单体架构项目完整开发文档,基于 Spring Boot 2.7.11 + JDK 11。预计 5 月 20 日前更新完成,有需要的胖友记得一键三连,关注主页 “瑞吉外卖” 专栏获取最新文章。
相关资料:https://pan.baidu.com/s/1rO1Vytcp67mcw-PDe_7uIg?pwd=x548
提取码:x548

文章目录

    • 1.文件上传介绍
    • 2.文件下载介绍
    • 3.文件上传代码实现
    • 4.文件下载代码实现

1.文件上传介绍

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

文件上传时,对页面的 form 表单通常有如下要求:

  • method="post":采用 POST 方式提交数据;
  • enctype="multipart/form-data":采用 multipart 格式上传文件;
  • type="file":使用 input 的 file 控件上传。

下面是一个简单例子:

<form method="post" action="/common/upload" enctype=="multipart/form-data">
    <input name="myFile" type="file"/>
    <input type="submit" value="提交"/>
</form>

目前一些前端组件库也提供了相应的上传组件,但是底层原理还是基于 form 表单的文件上传。例如 Element Ul 中提供的 upload 上传组件:

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

  • commons-fileupload
  • commons-io

可是直接使用 Apache 的两个组件进行开发有些繁琐,Spring 框架在 spring-web 包中基于 Apache 的两个组件对文件上传进行了封装,大大简化了服务端代码,我们只需要在 Controller 的方法中声明一个 MultipartFile 类型的参数即可接收上传的文件,例如:

@PostMapping("/upload")
public R<String> upload(MultipartFile file){
    ......
}

2.文件下载介绍

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

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

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

3.文件上传代码实现

对于文件上传,页面端可以使用 Element UI 提供的上传组件。可以直接使用下载的资料中提供的上传页面:

位置:瑞吉外卖\瑞吉外卖项目\资料\文件上传下载页面\upload.html

直接将 upload.html 复制到项目的 static/backend/page/demo/upload.html 路径下即可,需要处理的请求信息如下:

我们新建一个 controller/CommonController.java 类用于处理文件上传与下载相关的公共类,文件上传代码如下:

package cn.javgo.reggie_take_out.controller;

import cn.javgo.reggie_take_out.common.R;
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.util.UUID;

/**
 * 处理文件上传下载的控制器
 */
@RestController
@RequestMapping("/common")
public class CommonController {
    @Value("${reggie.upload-path}")
    private String uploadPath;

    /**
     * 文件上传
     * 说明:
     * 1.前端的 form 表单必须指定 enctype="multipart/form-data" 属性
     * 2.前端的 input 标签必须指定 name 属性,后端通过 name 属性获取文件
     * 3.file 是一个临时文件,上传成功后会自动删除,因此需要将文件拷贝到指定目录
     *
     * @param file 文件,必须与前端的 name 属性一致
     * @return R
     */
    @PostMapping("/upload")
    public R<String> upload(MultipartFile file){
        // 1.获取原始文件名
        String fileName = file.getOriginalFilename();
        // 2.截取文件扩展名
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        // 3.使用 UUID 生成文件名,防止重复
        String newFileName = UUID.randomUUID().toString() + suffix;
        // 4.创建文件对象
        File dir = new File(uploadPath);
        // 5.判断目录是否存在,不存在则创建
        if(!dir.exists()){
            dir.mkdirs();
        }
        // 6.将文件写入磁盘
        try {
            file.transferTo(new File(uploadPath + newFileName));
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 7.返回文件名,用于回显
        return R.success(newFileName);
    }
}

注释比较明确,就不一一展开说明了,上面需要注意的是代表文件对象的 MultipartFile 参数名必须与前端 input 标签的 name 属性一致:

上述代码中为了灵活配置文件保存位置,我们将存储路径配置到了 application 配置文件中,然后使用 @Value("${reggie.upload-path}") 进行注入,配置内容如下:

# reggie 相关配置
reggie:
  upload-path: D:\test\reggie\file

重启应用,登陆后再访问 http://localhost:8080/backend/page/demo/upload.html 页面进行上传测试:

查看对应保存位置,图片上传成功:

4.文件下载代码实现

在我们成功上传文件后,就会涉及到一个文件下载的问题,这样才能够实现上传成功后能够在页面看到刚才上传的图片,对应需要处理的请求信息如下:

对应的处理方法如下:

@RestController
@RequestMapping("/common")
public class CommonController {
	/**
     * 文件下载
     * @param name 文件名
     * @param response 响应对象
     */
    @GetMapping("/download")
    public void download(String name, HttpServletResponse response){
        // 1.创建文件对象
        File file = new File(uploadPath + name);
        // 2.判断文件是否存在
        if (file.exists()){
            try{
                // 3.使用输入流读取文件
                FileInputStream fis = new FileInputStream(file);
                // 4.使用输出流将文件写出
                ServletOutputStream sos = response.getOutputStream();
                // 5.设置响应类型为图片类型
                response.setContentType("image/jpeg");
                // 6.定义一个长度变量,用于存放每次读取的数据长度
                int len = 0;
                // 7.定义一个 1024 字节的缓冲区,用于存放每次读取的数据
                byte[] buffer = new byte[1024];
                // 8.循环将输入流中的数据读取到缓冲区中,(len = fis.read(buffer)) > 0 表示读取到数据
                while ((len = fis.read(buffer)) != -1){
                    // 9.使用输出流将缓冲区的数据输出到客户端浏览器
                    sos.write(buffer,0,len);
                    // 10.刷新输出流
                    sos.flush();
                }
                // 11.关闭资源
                sos.close();
                fis.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    
    // 省略其他方法
}

实现思路中规中矩,这里就不再进行解释了,看注释即可。下面重启应用再次进行图片上传测试(先登录后访问上传页面),上传完成后随后执行下载图片进行回显:

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

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

相关文章

pandas dataframe 中 explode()函数用法及效果

最近在使用pyspark处理数据&#xff0c;需要连接各种各样的表和字段&#xff0c;因此记录相关函数的使用情况。今天介绍explode(). 1. explode()函数简介 explode 函数是 pandas.DataFrame 类的一个方法&#xff0c;能够通过pyspark间接调用。 它可以将一个包含list或者其它可…

【零基础学JS - 7 】javaScript 中的8大数据类型

&#x1f468;‍&#x1f4bb; 作者简介&#xff1a;程序员半夏 , 一名全栈程序员&#xff0c;擅长使用各种编程语言和框架&#xff0c;如JavaScript、React、Node.js、Java、Python、Django、MySQL等.专注于大前端与后端的硬核干货分享,同时是一个随缘更新的UP主. 你可以在各个…

Python之selenium关于Chrome驱动位置,闪退的问题和安装路径

目录 零、查看Python的安装路径一、Chromedriver放置的位置二、浏览器闪退 零、查看Python的安装路径 一、Chromedriver放置的位置 背景&#xff1a;之前由于Chrome浏览器自动升级后&#xff0c;导致驱动与浏览器不匹配&#xff0c;自己也不知道问题出在哪儿&#xff0c;花费了…

hadoop和spark配置问题记录

hadoop和spark配置问题记录 Spark的WebUI访问不了 直接启动的start-all.sh是环境变量中配置的hadoop的脚本&#xff0c;不是spark的&#xff0c;因此启动spark的start-all.sh脚本即可。 Spark看不到Worker信息 启动Spark在UI界面上看不到worker节点的信息_潇洒哥WH3的博客-C…

【Java|基础篇】包和访问权限修饰符

文章目录 1. 前言2. 包的概念3. 包的创建以及优点4. 访问权限修饰符5. 总结 1. 前言 包和访问权限修饰符是Java中两个常用的概念&#xff0c;它们都与访问控制有关. 2. 包的概念 在面向对象体系中&#xff0c;提出了一个软件包的概念&#xff0c;即&#xff1a;为了更好的管…

代码随想录算法训练营第四十四天 | 完全背包、完全背包的遍历顺序

完全背包 理论基础 文档讲解&#xff1a;代码随想录 (programmercarl.com) 视频讲解&#xff1a;带你学透完全背包问题&#xff01; 和 01背包有什么差别&#xff1f;遍历顺序上有什么讲究&#xff1f;_哔哩哔哩_bilibili 完全背包和01背包问题唯一不同的地方就是&#xff0c;每…

品牌战略规划:如何让你的品牌脱颖而出

虽然很多国内企业家都非常重视战略&#xff0c;什么商业战略、渠道战略之类的一通研究&#xff0c;但惟独却缺乏对品牌战略的一个正确理解。 很多时候一把手不参与&#xff0c;也不关心品牌建设&#xff0c;经常上来就让团队实操动作&#xff0c;而自己只看最终结果&#xff0…

LeetCode20.有效的括号

题目 思路 每一个右括号应该与与在它左边最近的左括号相匹配&#xff0c;所以这道题可以通过栈实现 代码 &#x1f4ac;由于博主还没有学习到C&#xff0c;能力有限&#xff0c;所以只能自己实现一个栈&#xff0c;学过C的朋友可以直接使用CSTL中的栈来实现 #define _CRT_S…

深入理解事务的隔离性 —— 多版本并发控制( MVCC )

文章目录 一、数据库并发的三种场景二、多版本并发控制&#xff08;MVCC&#xff09;的初步认识三、事务的隐藏列字段四、UNDO 日志五、Read View 读视图六、深入理解 MVCC —— 隔离级别的实现原理七、RC 与 RR 的本质区别 一、数据库并发的三种场景 数据库并发指的是多个用户…

如何将Shapefile文件导入Sketch Up进行建模

涉及软件&#xff1a;ArcGIS10.4.1&#xff0c;Sketch Up Pro2022 1.shapefile的属性编辑 基于我们已有的shp文件&#xff0c;打开属性表&#xff0c;添加一个高度字段用于存放平面图形高度信息&#xff0c;为后期三维拉伸做好准备&#xff08;如果已有相关信息&#xff0c;请…

区间合并【pair、sort】

将有交集&#xff08;包括端点&#xff09;的区间进行合并 802. 区间和 - AcWing题库 例如&#xff0c;现在有这样一个数据 1 2 2 4 5 6 7 8 7 9最终合并的结果就是 1 —— 45 —— 67 —— 9 得到三个区间 第一步&#xff1a;按照区间左端点排序 第二步&#xff1a;扫描…

【C++初阶】:模板初阶

模板初阶 一.函数模板1.简单使用2.模板原理3.函数模板的实例化4.模板参数的匹配原则 二.类模板1.基本格式2.类模板实例化 一.函数模板 1.简单使用 函数模板代表了一个函数家族&#xff0c;该函数模板与类型无关&#xff0c;在使用时被参数化&#xff0c;根据实参类型产生函数的…

【OJ比赛日历】快周末了,不来一场比赛吗? #05.20-05.26 #10场

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 2023-05-20&#xff08;周六&#xff09; #4场比赛2023-05-21…

关于Compilation failed: internal java compiler error的解决方法(Idea)

**idea编译项目时出现java: Compilation failed: internal java compiler error java:编译失败&#xff1a;内部java编译器错误 根本原因基本上有两个&#xff0c;一个jdk版本问题&#xff0c;一个堆栈不足问题&#xff0c;我这次就是堆栈不足导致的&#xff1b; 1、项目过大…

IDOIEO盘点,包括PoseiSwap等即将面向市场的潜力打新活动

本月即将推出几个值得关注的打新活动一览&#xff1a; 项目名&#xff1a;PoseiSwap 官网&#xff1a;poseiswap.xyz PoseiSwap 是 Nautilus Chain 上的首个 DEX&#xff0c; Nautilus Chain 作为目前行业内第一个并行化且运行速度最快EVM Rollup的L3扩容方案&#xff0c;其将…

如何反转ggplot2中的图例键顺序

动动发财的小手&#xff0c;点个赞吧&#xff01; 在本教程[1]中&#xff0c;我们将学习如何反转 ggplot2 中图例键的顺序。 在 ggplot2 中&#xff0c;当我们在 aes() 中使用颜色或填充参数为变量着色时&#xff0c;我们会得到一个带有键的图例&#xff0c;显示哪些键匹配哪些…

蓝奏云直链解析

[玫瑰]蓝奏云直链获取在线解析网站源码 蓝奏云链接解析 本地API接口 [玫瑰]支持有无密码和短期直链和永久直链&#xff0c;同时还可以显示文件名和大小。 [玫瑰]这个解析器无需数据库即可搭建&#xff0c;API接口已经本地化&#xff0c;非常简单易用。 [玫瑰]解压密码 123321 链…

zotero-style 摸索

打开zotero-编辑-首选项-style 可以看到图中所示的function界面 标签 刚安装好的时候原来自带的标签都不显示了&#xff0c;重启也没用&#xff0c;原来是不要勾选function下的title就好了 还有一种解决方法是&#xff0c;同时勾选title和tag&#xff0c;然后在标题栏右键&am…

【数据结构】堆(Heap)

文章目录 前言一、堆1、 概念2、性质3、结构 二、堆的实现1、算法实现&#xff1a;向下调整算法向上调整算法(堆的创建)堆的插入堆的删除堆的排序 2、 代码实现(小堆)&#xff1a;堆的定义交换检查容量向下调整向上调整堆的初始化堆的创建销毁堆堆的插入堆的删除获取堆顶元素判…

Linux下C/C++实现DNS查询(DNS QUERY)

DNS 的全称是 Domain Name System 或者 Domain Name Service&#xff0c;它主要的作用就是将人们所熟悉的网址 (域名) “翻译”成电脑可以理解的 IP 地址&#xff0c;这个过程叫做 DNS 域名解析。域名是由一串用点分隔的名字组成的 Internet 上某一台计算机或计算机组的名称&am…