若依-了解头像(文件)上传流程

2023-11-07

周知

本文主要记录本人关于自主学习若依框架的部分心得经验,必定有许多不足甚至理解有误的部分,如果对你有帮助我也不胜欢喜;如果发现有错误的地方也希望能够分享指出,一起加油

需求原因

客户内部系统,一般不对外开放,因此不适合将文件(如图片)存放到云上,所以存放在本地服务器中

流程分析

每个(组)截图会配上本人的理解和分析,有不对或不全的地方万望指出

页面

在这里插入图片描述

登录后任意界面点击右上角头像位置选择进入个人中心,选择点击头像位置弹出该窗口,选择本地图片上传;
前端调用接口完成头像更新,在数据库的体现为头像字段内容由空转为图片保存地址
在这里插入图片描述
存取地址默认会将profile替换为若依后端配置的默认文件夹地址
/profile ——> D:/ruoyi/uploadPath

前端

ruoyi-ui\src\views\system\user\profile\userAvatar.vue
在这里插入图片描述
ruoyi-ui\src\api\system\user.js
在这里插入图片描述

若依前端不进行什么处理,负责将页面接收到的文件调用后端接口传到后端,并将图片的读取地址改为后端传回来的保存地址

后端

设计到的类中代码可能过多不便全部截出,附上相对路径可自行查看
src\main\java\com\ruoyi\web\controller\system\SysProfileController.java
在这里插入图片描述

后端获取到前端传来的文件,进行通用的校验和处理
判空、拼接存储地址、更新用户信息(头像地址)、更新token、返回头像地址

src\main\java\com\ruoyi\common\utils\file\FileUploadUtils.java
在这里插入图片描述

若依的文件工具类对传来的文件进行校验和处理
判断文件名是否过长、扩展名(文件类型)是否规范合法、拼接存储地址、调用jar包类和方法进行文件的保存
———————
后面还有两个重载的方法,实际上并不进行什么处理,只是为了应对其他使用场景(如不传)存储文件夹、扩展名等……最终都是调用上述方法进行存储操作

在这里插入图片描述在这里插入图片描述

涉及到的jar包、内置类

jetbrains://idea/navigate/reference?project=ifs_7_0_java&path=E:/Maven/develop/apache-maven-3.5.3/repository/org/springframework/spring-web/5.3.20/spring-web-5.3.20.jar!/org/springframework/web/multipart/MultipartFile.class
在这里插入图片描述

这是是关于以上截图代码中调用到的getOriginalFilename()和transferTo() 方法;
transferTo() 调用到FileCopyUtils工具类进行文件读写以实现头像的本地保存

jetbrains://idea/navigate/reference?project=ifs_7_0_java&path=E:/Maven/develop/apache-maven-3.5.3/repository/org/springframework/spring-core/5.3.20/spring-core-5.3.20.jar!/org/springframework/util/FileCopyUtils.class
在这里插入图片描述

若依文件工具类调用该类的方法进行文件的读写

java\io\OutputStream.java
在这里插入图片描述

该方法为上一张图中的 out.weite(in); 所调用,进行文件写入

jetbrains://idea/navigate/reference?project=ifs_7_0_java&path=E:/Maven/develop/apache-maven-3.5.3/repository/org/springframework/spring-core/5.3.20/spring-core-5.3.20.jar!/org/springframework/util/StreamUtils.class
在这里插入图片描述

同样是对于文件的输入输出流操作;
nutNull() 方法:如果入参1为空,返回message为入参2的异常

附上若依默认文件地址的配置

src\main\resources\application.yml
在这里插入图片描述

新增需求

强化文件类型校验

思路

除了后缀校验之外,另外增加文件真实类型校验

资料

https://blog.csdn.net/qq_42764468/article/details/121522645

实现

src\main\java\com\ruoyi\common\utils\file\FileTypeEnum.java

package com.ruoyi.common.utils.file;

import lombok.Getter;
import java.util.ArrayList;
import java.util.List;

@Getter
public enum FileTypeEnum {
    /**
     * 允许上传的附件类型集合
     */
    // 主要是图片,其他的先注释掉了
    JPEG("jpeg", "FFD8FF"),
    JPG("jpg", "FFD8FF"),
    PNG("png", "89504E47"),
    BMP("bmp", "424D");
//    RTF("rtf", "7B5C727466"),
//    DOC("doc", "D0CF11E0"),
//    DOCX("docx", "504B030414"),
//    PDF("pdf", "255044462D312E");

    /**
     * 允许上传的文件类型的文件后缀
     */
    private final String suffixName;

    /**
     * 允许上传的文件类型的文件头信息
     */
    private final String headCode;

    /**
     * 构造方法
     *
     * @param suffixName 文件后缀名
     * @param headCode   文件头信息
     */
    FileTypeEnum(String suffixName, String headCode) {
        this.suffixName = suffixName;
        this.headCode = headCode;
    }

    /**
     * 获取允许上传的文件类型集合
     *
     * @return List-String
     */
    public static List<String> getFileType() {
        List<String> fileTypeList = new ArrayList<>();
        for (FileTypeEnum fileTypeEnum : FileTypeEnum.values()) {
            fileTypeList.add(fileTypeEnum.getSuffixName());
        }
        return fileTypeList;
    }
}


src\main\java\com\ruoyi\common\utils\file\FileHeaderUtils.java

package com.ruoyi.common.utils.file;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.ruoyi.common.exception.file.InvalidExtensionException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Slf4j
public class FileHeaderUtils {
    /**
     * 文件类型和文件大小校验
     *
     * @param file            上传的附件
     * @param allowedFileType 限制上传附件的类型
     */
    public static String checkFileHeader(MultipartFile file, Set<String> allowedFileType) throws InvalidExtensionException {
        // 去掉了后缀校验和大小校验
        String fileType;
        String fileName = file.getOriginalFilename();
        // 为了防止报错增加的两个变量
        String fileHeader = "其他类型";
        String[] alloweds = new String[allowedFileType.size()];
        // 给alloweds赋值
        int i = 0;
        for (String s : allowedFileType) {
            alloweds[i] = s;
            i++;
        }
        // 文件类型判断 - 校验文件头内容
        try (InputStream inputStream = file.getInputStream()) {
            // 获取到上传文件的文件头信息
            fileHeader = FileHeaderUtils.getFileHeader(inputStream);

            if (StringUtils.isBlank(fileHeader)) {
                log.error("Failed to get file header content.");
                throw new InvalidExtensionException(alloweds, fileHeader, fileName);
//                throw new CommonException(BizCodeEnum.FILE_UPLOAD_TYPE_NOT_ALLOWED);
            }
            // 根据上传文件的文件头获取文件的真实类型
            fileType = getFileType(fileHeader);
            if (StringUtils.isBlank(fileType) || !fileTypeAllowed(fileType, allowedFileType)) {
                log.error("Unsupported file type: [{}]", fileType);
                throw new InvalidExtensionException(alloweds, fileType, fileName);
//                throw new CommonException(BizCodeEnum.FILE_UPLOAD_TYPE_NOT_ALLOWED);
            }
        } catch (IOException | InvalidExtensionException e) {
            log.error("Get file input stream failed.", e);
            throw new InvalidExtensionException(alloweds, fileHeader, fileName);
//            throw new CommonException(BizCodeEnum.ATTACHMENT_UPLOAD_ERROR);
        }
        return fileType;
    }

    /**
     * 文件类型校验
     *
     * @param fileType    待校验的类型
     * @param allowedType 允许上传的文件类型
     * @return true - 满足,false - 不满足
     */
    private static boolean fileTypeAllowed(String fileType, Set<String> allowedType) {
        if (StringUtils.isBlank(fileType) || CollectionUtils.isEmpty(allowedType)) {
            return false;
        }
        return allowedType.contains(fileType);
    }

    /**
     * 据文件的头信息获取文件类型
     *
     * @param fileHeader 文件头信息
     * @return 文件类型
     */
    public static String getFileType(String fileHeader) {
        if (fileHeader == null || fileHeader.length() == 0) {
            return null;
        }
        fileHeader = fileHeader.toUpperCase();
        FileTypeEnum[] fileTypes = FileTypeEnum.values();
        for (FileTypeEnum type : fileTypes) {
            boolean b = fileHeader.startsWith(type.getHeadCode());
            if (b) {
                return type.getSuffixName();
            }
        }
        return null;
    }

    /**
     * 文件头字节数组转为十六进制编码
     *
     * @param content 文件头字节数据
     * @return 十六进制编码
     */
    private static String bytesToHexString(byte[] content) {
        StringBuilder builder = new StringBuilder();
        if (content == null || content.length <= 0) {
            return null;
        }
        String temp;
        for (byte b : content) {
            temp = Integer.toHexString(b & 0xFF).toUpperCase();
            if (temp.length() < 2) {
                builder.append(0);
            }
            builder.append(temp);
        }
        return builder.toString();
    }

    /**
     * 获取文件的文件头信息
     *
     * @param inputStream 输入流
     * @return 文件头信息
     * @throws IOException 异常
     */
    private static String getFileHeader(InputStream inputStream) throws IOException {
        byte[] content = new byte[28];
        inputStream.read(content, 0, content.length);
        return bytesToHexString(content);
    }
}


src\main\java\com\ruoyi\common\utils\file\FileUploadUtils.java

/**
     * 文件大小校验
     *
     * @param file 上传的文件
     * @return
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws InvalidExtensionException
     */
    public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, InvalidExtensionException
    {
        // 文件大小
        long size = file.getSize();
        // 不能超过最大值,默认50M
        if (size > DEFAULT_MAX_SIZE)
        {
            throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
        }
        // 获取文件名、--扩展名--
        String fileName = file.getOriginalFilename();
        String extension = getExtension(file);
        // 扩展名非空但不被允许——>判断非法类型
        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension))
        {
            if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
                        fileName);
            }
            else
            {
                throw new InvalidExtensionException(allowedExtension, extension, fileName);
            }
        }
        // 获取到允许文件头
        List<String> fileType = FileTypeEnum.getFileType();
        Set<String> typeSet = new HashSet<>();
        for (String s : fileType) {
            // 添加到Set中
            typeSet.add(s);
        }
        // 校验文件头
        FileHeaderUtils.checkFileHeader(file, typeSet);
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

若依-了解头像(文件)上传流程 的相关文章

  • Spring Batch 多线程 - 如何使每个线程读取唯一的记录?

    这个问题在很多论坛上都被问过很多次了 但我没有看到适合我的答案 我正在尝试在我的 Spring Batch 实现中实现多线程步骤 有一个包含 100k 条记录的临时表 想要在 10 个线程中处理它 每个线程的提交间隔为 300 因此在任何时
  • Java EE:如何获取我的应用程序的 URL?

    在 Java EE 中 如何动态检索应用程序的完整 URL 例如 如果 URL 是 localhost 8080 myapplication 我想要一个可以简单地将其作为字符串或其他形式返回给我的方法 我正在运行 GlassFish 作为应
  • 如何在 Play java 中创建数据库线程池并使用该池进行数据库查询

    我目前正在使用 play java 并使用默认线程池进行数据库查询 但了解使用数据库线程池进行数据库查询可以使我的系统更加高效 目前我的代码是 import play libs Akka import scala concurrent Ex
  • 给定两个 SSH2 密钥,我如何检查它们是否属于 Java 中的同一密钥对?

    我正在尝试找到一种方法来验证两个 SSH2 密钥 一个私有密钥和一个公共密钥 是否属于同一密钥对 我用过JSch http www jcraft com jsch 用于加载和解析私钥 更新 可以显示如何从私钥 SSH2 RSA 重新生成公钥
  • 在 HTTPResponse Android 中跟踪重定向

    我需要遵循 HTTPost 给我的重定向 当我发出 HTTP post 并尝试读取响应时 我得到重定向页面 html 我怎样才能解决这个问题 代码 public void parseDoc final HttpParams params n
  • Final字段的线程安全

    假设我有一个 JavaBeanUser这是从另一个线程更新的 如下所示 public class A private final User user public A User user this user user public void
  • Android:捕获的图像未显示在图库中(媒体扫描仪意图不起作用)

    我遇到以下问题 我正在开发一个应用程序 用户可以在其中拍照 附加到帖子中 并将图片保存到外部存储中 我希望这张照片也显示在图片库中 并且我正在使用媒体扫描仪意图 但它似乎不起作用 我在编写代码时遵循官方的Android开发人员指南 所以我不
  • 多个 Maven 配置文件激活多个 Spring 配置文件

    我想在 Maven 中构建一个环境 在其中我想根据哪些 Maven 配置文件处于活动状态来累积激活多个 spring 配置文件 目前我的 pom xml 的相关部分如下所示
  • 控制Android的前置LED灯

    我试图在用户按下某个按钮时在前面的 LED 上实现 1 秒红色闪烁 但我很难找到有关如何访问和使用前置 LED 的文档 教程甚至代码示例 我的意思是位于 自拍 相机和触摸屏附近的 LED 我已经看到了使用手电筒和相机类 已弃用 的示例 但我
  • 反射找不到对象子类型

    我试图通过使用反射来获取包中的所有类 当我使用具体类的代码 本例中为 A 时 它可以工作并打印子类信息 B 扩展 A 因此它打印 B 信息 但是当我将它与对象类一起使用时 它不起作用 我该如何修复它 这段代码的工作原理 Reflection
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • 如何在PreferenceActivity中添加工具栏

    我已经使用首选项创建了应用程序设置 但我注意到 我的 PreferenceActivity 中没有工具栏 如何将工具栏添加到我的 PreferenceActivity 中 My code 我的 pref xml
  • 十进制到八进制的转换[重复]

    这个问题在这里已经有答案了 可能的重复 十进制转换错误 https stackoverflow com questions 13142977 decimal conversion error 我正在为一个类编写一个程序 并且在计算如何将八进
  • Java按日期升序对列表对象进行排序[重复]

    这个问题在这里已经有答案了 我想按一个参数对对象列表进行排序 其日期格式为 YYYY MM DD HH mm 按升序排列 我找不到正确的解决方案 在 python 中使用 lambda 很容易对其进行排序 但在 Java 中我遇到了问题 f
  • Java TestNG 与跨多个测试的数据驱动测试

    我正在电子商务平台中测试一系列商店 每个商店都有一系列属性 我正在考虑对其进行自动化测试 是否有可能有一个数据提供者在整个测试套件中提供数据 而不仅仅是 TestNG 中的测试 我尝试不使用 testNG xml 文件作为机制 因为这些属性
  • AWS 无法从 START_OBJECT 中反序列化 java.lang.String 实例

    我创建了一个 Lambda 函数 我想在 API 网关的帮助下通过 URL 访问它 我已经把一切都设置好了 我还创建了一个application jsonAPI Gateway 中的正文映射模板如下所示 input input params
  • Eclipse Java 远程调试器通过 VPN 速度极慢

    我有时被迫离开办公室工作 这意味着我需要通过 VPN 进入我的实验室 我注意到在这种情况下使用 Eclipse 进行远程调试速度非常慢 速度慢到调试器需要 5 7 分钟才能连接到远程 jvm 连接后 每次单步执行断点 行可能需要 20 30
  • 如何从终端运行处理应用程序

    我目前正在使用加工 http processing org对于一个小项目 但是我不喜欢它附带的文本编辑器 我使用 vim 编写所有代码 我找到了 pde 文件的位置 并且我一直在从 vim 中编辑它们 然后重新打开它们并运行它们 重新加载脚
  • Firebase 添加新节点

    如何将这些节点放入用户节点中 并创建另一个节点来存储帖子 我的数据库参考 databaseReference child user getUid setValue userInformations 您需要使用以下代码 databaseRef
  • 使用 xpath 和 vtd-xml 以字符串形式获取元素的子节点和文本

    这是我的 XML 的一部分

随机推荐

  • 【可解释性机器学习】interpretable machine learning——Functional Decomposition

    Functional Decomposition 机器学习中的监督学习模型可被视为一个函数 该函数将高维特征向量作为输入 然后产生预测或分类概率作为输出 函数分解 functional decomposition 分解高维函数并将其表达为可
  • 常见前端安全问题概述

    随着互联网的高速发展 安全问题已成为企业最关注的焦点之一 而前端又是容易引发安全问题的 窗口 作为开发人员的我们 更需要充分了解如何预防和修复安全漏洞 本文将列举常见的前端安全问题 希望对你有所帮助 本文将从以下几种安全问题展开介绍 XSS
  • Jmeter中csvRead函数的使用

    当想要脚本随机传参数时 可以结合csvRead和Random函数使用 也相当于参数化的一种用法 想了解其他参数化方法的读者 可以参考上一篇文档 1 事先准备好存储数据的文档 注意 数据之间使用英文逗号隔开 否则后面随机抽取数据时没有抽取到
  • 【yarn】yarn Job 状态机正常执行流程

    1 概述 今天6月1日 节日值得发个文章 庆祝一下 上一篇文章 Yarn Yarn Service端如何处理客户端提交的任务 在上一篇文章中 我们知道服务器接收到客户端提交的任务之后 会启动多个状态机进行联合操作 最终来解决任务提交之后的全
  • JS内置对象Math的常用方法

    JS内置对象Math的常用方法 1 向上取整 Math ceil 2 向下取整 Math floor 3 四舍五入 Math round 4 绝对值 Math abs 最大值 Math max 最小值 Math min 7 随机数 Math
  • 5 种方法重复执行历史命令

    Linux 下 如果要执行一条或多条之前输过的指令 要怎么处理 很多人会想到使用上下箭头去翻查历史输入的命令 这当然是可以了 除了这种方法 本文再介绍另外 5 种方法来实现这样的效果 在正式开始之前 我们先随便执行几条命令 便于后面演示 为
  • Anaconda 更换镜像源、创建虚拟环境并安装 PyTorch

    文章目录 1 更换镜像源 2 选择合适的 CUDA Runtime 版本 2 1 CUDA Runtime 版本不能高于 CUDA Driver 版本 2 2 根据显卡的算力和架构确定 CUDA Runtime 版本 2 3 找到安装 Py
  • Ps2021软件安装Mac(含M1)、Windows版

    一 下载链接 Mac版本 1 M1芯片适用 百度网盘下载地址 提取码 rpyn 2 M1不适用 系统10 15及以上 下载地址 Windows版本 Ps2022 64位 此版本不支持LTSC版本系统 百度网盘下载地址 提取码 wb2d 二
  • MySQL: 运算符使用练习

    前言 练习运算符的使用 加强记忆 案例目的 在已建数据库中创建数据表 并对表中数据进行处理 练习运算符 包括数据运算符 逻辑运算符 位运算符 的使用 操作过程 创建名为 tb15 的数据表 包含 VARCHAR 100 类型的字段 note
  • 在vue中使用this.$refs报错undefined找不到子组件的方法

    最近遇见个问题 在父组件调用子组件的方法时报错undefined 这种情况一般都是父组件调用子组件方法的时候 dom还没更新成功 可以使用 this nextTick 这个方法作用是当数据被修改后使用这个方法会回调获取更新后的dom再渲染出
  • Spring MVC视图解析器

    Spring MVC视图解析器 AbstractCachingViewResolver XmlViewResolver ResourceBundleViewResolver UrlBasedViewResolver InternalReso
  • 报错Caused by java lang ClassNotFoundException Didn't find c

    分享一下我老师大神的人工智能教程 零基础 通俗易懂 风趣幽默 还带黄段子 希望你也加入到我们人工智能的队伍中来 https blog csdn net jiangjunshow 报错信息 03 14 20 24 09 675 15801 1
  • 赛码-接金币-java

    题目描述 小赛非常喜欢玩游戏 最近喜欢上了一个接金币的游戏 在游戏中 使用帽子左右移动接金币 金币接的越多越好 但是金币掉到地上就不能再接了 为了方便问题的描述 我们把电脑屏幕分成11格 帽子每次能左右移动一格 现在给电脑屏幕如图标上坐标
  • 浅谈systemd

    浅谈systemd systemd的基本概念 Systemd的管理服务 Systemd的管理运行级别 systemd的基本概念 一 system的简要介绍 定义 系统启动和服务器守护进程管理器 负责在系统启动或运行时 激活系统资源 服务器进
  • 使用Docker安装Portainer

    打开powershell输入命令 docker run d p 9000 9000 restart always v var run docker sock var run docker sock name prtainer portain
  • 双轮平衡车实现自平衡功能

    1 功能说明 在双轮小车上安装一个六轴陀螺仪传感器 本文示例将实现双轮小车自主平衡功能 2 电子硬件 在这个示例中 我们采用了以下硬件 请大家参考 主控板 Basra主控板 兼容Arduino Uno 扩展板 Bigfish2 1扩展板 传
  • 【Java】Java中的“引用类型”和C中的“指针”区别

    文章目录 前言 1 Java中 基本数据类型 和 引用数据类型 变量在内存分配时的不同 2 C中 指针 的特点 3 Java中 引用 的特点 4 Java的参数传递 5 参考 前言 在学习Java中变量的数据类型时 发现其分为2大类 基本数
  • spring加载流程之ConfigurationClassPostProcessor

    spring加载流程之ConfigurationClassPostProcessor ConfigurationClassPostProcessor postProcessBeanDefinitionRegistry processConf
  • FPGA面试真题解析

    FPGA面试真题解析 1 1 十进制46 25对应的二进制表达式为 硬件逻辑实习岗 A 101110 11 B 101101 01 C 101110 1 D 101110 01 解析 这个问题看上去很简单 那是因为我们平时可以打开电脑上的计
  • 若依-了解头像(文件)上传流程

    周知 本文主要记录本人关于自主学习若依框架的部分心得经验 必定有许多不足甚至理解有误的部分 如果对你有帮助我也不胜欢喜 如果发现有错误的地方也希望能够分享指出 一起加油 需求原因 客户内部系统 一般不对外开放 因此不适合将文件 如图片 存放