Spring Boot 项目如何实现上传头像功能?

news2024/9/21 18:41:32

目录

设计思路

效果展示

​编辑

分析

前后端交互接口

请求

响应

代码实现和详细注释

数据库设计

自定义资源映射

前后端交互

客户端开发

服务器开发


设计思路


效果展示

 

分析

实现这个功能只要弄清楚以下几点即可:

  1. 怎么将头像数据发送给服务器,设定怎样的数据类型?我们可以使用 form 表单将数据进行发送,表单设定格式为 enctype="multipart/form-data"(这也是为什么不直接用 ajax 请求后端,因为 ajax 发送请求的数据格式很难设置成这个数据格式),使用 type="file" 的 input 控件并搭配 type="submit" 的 input 控件来实现用户头像的上传。
  2. form 表单提交数据后,怎么接收服务器的响应?使用 FormData 对象来控制 form 表单,通过  FormData 对象的 append 方法即可将 input 控件中的头像数据保存到 FormData 对象中,这时候我们便可以使用 ajax 的方法请求后端,就可以接收服务器响应啦,请求中的数据便是 FormData 对象(相当于对 form 表单的一层封装,只是用其格式类型),这时还需要注意,一定要将 contentType 和 processData 属性设为 false,这样才能避开 ajax 默认发送格式,而使用 form 表单设置的发送数据格式。
  3. 服务器如何搭配数据库保存头像数据?数据库不需要完整的将图片数据保存下来,具体如何实现呢,我们可以通过 transferTo 文件操作将图片保存到我们本地磁盘上,然后图片保存的绝对路径以字符串的形式保存到数据库即可(使用 UUID 生成随机字符串保存,否则可能会因为重名而覆盖图片文件)。这样,每次读取图片,通过这个路径即可(注意:最后这句话是一个误区,后面会分析原因)。
  4. 服务器保存头像后浏览器即使刷新,头像也没有发生变化?因为浏览器禁止 js 访问本地磁盘,而我们在数据库中保存的是头像的绝对路径,那么肯定是读取不到的。怎么办呢?我们可以设置两个路径,一个是绝对路径(供服务器保存头像),一个是相对路径(供浏览器访问头像),因此数据库保存头像的相对路径即可。
  5. 明明按照以上步骤在浏览器上传了图片并保存到了本地磁盘,但是页面图片还是没有更新,需要重启服务器后才会更新图片?这是因为浏览器在界面上传图片,而 Spring Boot 程序是感知不到的,因此需要我们去设定自定义资源映射(具体的,往后看~)。

前后端交互接口

请求

POST /user/subphoto
Content-Type: multipart/form-data
processData: false

data: new FormData(document.querySelector("#forminfo")) //form 表单对象

响应

HTTP/1.1 200 OK
Content-Type: application/json

{
    "code": 200,
    "msg": "",
    "data": photoPathRelative //图片相对路径
}

代码实现和详细注释


数据库设计

用户表设计如下:

create table userinfo(
    id int primary key auto_increment,
    username varchar(100) unique,
    password varchar(65) not null,
    photo varchar(500) default "img/default.jpg",
    createtime timestamp default current_timestamp,
    updatetime timestamp default current_timestamp,
    `state` int default 0,
    nickname varchar(50) not null,
    realname varchar(50) default "",
    idcard varchar(50) default "",
    gitee varchar(200) default ""
) default charset 'utf8mb4';

自定义资源映射

创建一个类,名为 StaticResourcesConfig,实现 WebMvcConfigurer 接口下的 addResourceHandlers 方法,此方法就是用来注册路由的。

代码如下:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration //一定不要忘了这个注解
public class StaticResourcesConfig implements WebMvcConfigurer {


    /**
     * 自定义资源映射
     * 由于在浏览器界面上传图片,而 Spring boot 程序是感知不到的,因此需要自定义资源映射
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/upload/**")
                .addResourceLocations("file:upload/");
    }
}

addResourceHandler("/upload/**"):表示访问的时候路径上要加上upload,不然访问不到。

addResourceLocations("file:upload/"):你图片上传的路径,我的图片上传就在upload文件中。

前后端交互

客户端开发

        //隐藏修改头像和提交控件
        jQuery("#choose-photo").hide();
        jQuery("#sub-photo").hide();
        //修改头像
        function updatePhoto() {
            //点击头像,就会弹出文件选择框
            jQuery("#choose-photo").click();
            //当 #choose-photo 内容改变时触发
            jQuery("#choose-photo").change(function () {
                // //点击 submit 提交表单
                var fd = new FormData(document.querySelector("#forminfo"));
                fd.append("myphoto", jQuery("#choose-photo").val());
                jQuery.ajax({
                    type: "POST",
                    url: "/user/subphoto",
                    data: fd,
                    // 想让文件传输格式为 form 表单设置的格式,那么就必须设置以下两个属性
                    contentType: false,
                    processData: false,
                    success: function (result) {
                        if (result != null && result.code == 200) {
                            //刷新网页
                            alert("头像修改成功!");
                            location.href = location.href;
                        } else {
                            alert("头像上传失败,请稍后重试!");
                        }
                    }
                });

            });
        }

服务器开发

    /**
     * 修改用户头像
     * @param request
     * @param photo
     * @return
     */
    @RequestMapping("/subphoto")
    public AjaxResult updatePhoto(HttpServletRequest request,
                                  @RequestPart("myphoto") MultipartFile photo) {
        //非空校验
        if(photo == null) {
            return AjaxResult.fail(403, "图片错误!");
        }
        //要上传的文件名
        String originalFileName = photo.getOriginalFilename();

        //获取用户信息(1.删除旧头像需要原来的旧头像的路径 2.修改图片需要用户 id)
        UserInfo userInfo = UserSessionUtils.getUser(request);
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
                !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(403, "参数错误!");
        }
        if(!userInfo.getPhoto().equals("img/default.jpg")) {
            //不是修改默认头像的话就将旧头像删除即可
            File file = new File(AppVariable.IMG_PATH_ABSOLUTE
                    + userInfo.getPhoto().split("/")[2]);
            file.delete();
        }

        //获取文件后缀
        String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));
        //生成图片名称,使用 UUID 避免相同图片名冲突,加上图片后缀
        String photoName = UUID.randomUUID().toString() + suffix;

        //图片保存路径(绝对路径/相对路径)
        //这里为什么还要用相对路径,只用绝对路径不行么?
        //因为禁止浏览器访问本地磁盘绝对路径路径,因此最简单的办法就是配置一个绝对路径用来保存文件,一个相对路径提供给浏览器访问
        String photoPathAbsolute = AppVariable.IMG_PATH_ABSOLUTE + photoName; //绝对路径
        String photoPathRelative = AppVariable.IMG_PATH_RELATIVE + photoName; //相对路径
        //生成文件(绝对路径)
        File saveFile = new File(photoPathAbsolute);
        try {
            //将上传文件绝对路径保存到服务器文件系统
            photo.transferTo(saveFile);
            //保存图片相对路径到数据库中
            userService.updatePhotoById(userInfo.getId(), photoPathRelative);
            //修改评论
            userInfo.setPhoto(photoPathRelative);
            updateInfoUtils.updateSessionAndComment(request, userInfo);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //将图片相对路径返回给前端
        return AjaxResult.success(photoPathRelative);
    }

 

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

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

相关文章

九龙证券|服务器龙头获资金连续抢筹,尾盘主力抢筹前期大热门股

今天,核算机职业取得主力大手笔抢筹。 今天主力资金净流出53.89亿元,其间创业板净流出3.19亿元,沪深300成份股净流出7.61亿元。 申万一级职业中,今天有19个职业上涨,传媒职业接连两日均涨近5%,居首位&…

stm32当中GPIO输出知识点汇总(GPIO的八种模式及其原理)

一、GPIO工作模式. 1. 四种输入模式 GPIO_Mode_IN_FLOATING 浮空输入模式 GPIO_Mode_IPU 上拉输入模式 GPIO_Mode_IPD 下拉输入模式 GPIO_Mode_AIN 模拟输入模式 2. 四种输出模式 GPIO_Mode_Out_OD 开漏输出模式 GPIO_Mode_Out_PP 推挽输出模式 GPIO_Mod…

【剑指offer-C++】JZ79:判断是不是平衡二叉树

【剑指offer-C】JZ79:判断是不是平衡二叉树题目描述解题思路题目描述 描述:输入一棵节点数为 n 二叉树,判断该二叉树是否是平衡二叉树。 在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树。 平衡二叉树…

Nginx实现负载均衡的多种方法演示

文章目录前言一、配置讲解1.1 轮询算法(默认)1.2 IP_HASH算法1.3 Weighted算法1.4 URL_HASH算法总结前言 Nginx是一款高性能的Web服务器和反向代理服务器,它具有占用内存小、并发处理能力强、稳定性高等优点,适用于高并发、高负载的Web应用场…

pandas之DataFrame基础

pandas之DataFrame基础1. DataFrame定义2. DataFrame的创建形式3. DataFrame的属性4. DataFrame的运算5. pandas访问相关操作5.1 使用 loc[]显示访问5.2 iloc[] 隐式访问5.3 总结6. 单层索引和多层级索引6.1 索引种类与使用6.2 索引相关设置6.3 索引构造6.4 索引访问6.5 索引变…

BGP选路实验(重点是各种策略)

实验拓扑 基础配置(完成IGP的配置) 首先完成各 个接口IP地址,环回接口地址等一些基本配置,实现直连之间的互相通信 在R4,R5上的配置类似 [Huawei]int g0/0/0 [Huawei-GigabitEthernet0/0/0]ip add 192.168.1.1 24 [Huawei-GigabitEthernet0/0/0]int g0/0…

低代码是什么意思

此前,阿里云智能总裁张建锋曾在钉钉发布会上表示:“未来的软件开发一定是碎片化的,低代码开发将成为未来几年的行业关键词。”这句话表明了低代码在过去两年的火爆程度,预示着低代码有望成为软件领域的新风口。 那低代码是什么意…

chatgpt智能提效职场办公-excel表格6-6-6格式怎么设置(数字按照三个数字一组进行分隔)

chatgpt智能提效职场办公-excel表格6-6-6格式怎么设置(数字按照三个数字一组进行分隔) 作者:虚坏叔叔 博客:https://xuhss.com 早餐店不会开到晚上,想吃的人早就来了!😄 在Excel表格中设置6-6-…

移除元素(数组篇)

27. 移除元素 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。 不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑数组中…

004:Mapbox GL设定不同的style,更换底图形态

第004个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中设定不同的style,更换底图形态 。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共70行)相关API参考:专栏目标示例效果

「业务架构」需求工程—需求规范(第3部分)

将用户和系统需求记录到文档中。需求规范它是将用户和系统需求写入文档的过程。需求应该是清晰的、容易理解的、完整的和一致的。在实践中,这是很难实现的,因为涉众以不同的方式解释需求,并且在需求中经常存在固有的冲突和不一致。正如我们之…

GraphInsight 拓扑图体验

蚂蚁数据可视化 | G6📚前言📚使用说明📕数据规则节点的属性📚前言 蚂蚁数据可视化 G6 图可视化引擎是一个简单、易用、完备的图可视化引擎,它在高定制能力的基础上,提供了一系列设计优雅、便于使用的图可视…

1~3年的测试工程师薪资陷入了瓶颈期,如何突破自己实现涨薪?

对于技术人员而言,职业规划一般分为两个方向:做技术、做管理。进入软件测试行业的新人都会从最基础的执行开始,然后是基本的功能测试。 随后大家会根据个人职业发展来进一步细化,有的走管理路线,成为主管、经理、项目…

《操作系统》by李治军 | 实验3 - 系统调用

目录 一、实验内容 二、实验准备 1、系统调用的具体流程 (一)调用接口函数 API (二)触发 0x80 号中断 (三)跳转到 system_call 函数 (四)执行系统调用函数 sys_xxx 2、总结概…

19从零开始学Java之局部变量和成员变量是怎么回事?

作者:孙玉昌,昵称【一一哥】,另外【壹壹哥】也是我哦 千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者 前言 在前两篇文章中,壹哥给大家讲解了Java里的条件分支,包括if和switch两种情况。…

23.Spring练习(spring、springMVC)

目录 一、Spring练习环境搭建。 (1)设置服务器启动的展示页面。 (2)创建工程步骤。 (3)applicationContext.xml配置文件。 (4)spring-mvc.xml配置文件。 (5&#x…

Java集合——Set接口学习总结

一、HashSet实现类 1.常用方法 增加&#xff1a;add(E e)删除&#xff1a;remove(Object o)、clear()修改&#xff1a;查看&#xff1a;iterator()判断&#xff1a;contains(Object o)、isEmpty()常用遍历方式&#xff1a;Set<String> set new HashSet<String>()…

redis中布隆过滤器使用详解

文章目录一、布隆过滤器介绍1、什么是布隆过滤器2、布隆过滤器实现原理3、误判率4、布隆过滤器使用场景5、哈希表与布隆过滤器比较二、redis中布隆过滤器实战1.引入redisson依赖2.创建订单表3.配置redis4.配置BloomFilter5.创建订单6.单元测试总结一、布隆过滤器介绍 1、什么是…

什么是汽车以太网?

总目录链接>> AutoSAR入门和实战系列总目录 总目录链接>> AutoSAR BSW高阶配置系列总目录 文章目录什么是汽车以太网&#xff1f;汽车以太网市场中使用的标准和剖析汽车以太网类型什么是汽车以太网&#xff1f; 本页介绍了汽车以太网的基本特性并提到了汽车以…

【数据库】关系数据库

1.选择关系&#xff08;对行操作&#xff09; 2.投影&#xff08;对列操作&#xff09; &#xff08;行记录重复的不再显示&#xff09; 3.连接&#xff08;从两个关系的笛卡尔积中选出属性间满足一定条件的元组&#xff09; a.等值连接 b.自然连接&#xff08;等值连接的特殊…