本文主要记录本人关于自主学习若依框架的部分心得经验,必定有许多不足甚至理解有误的部分,如果对你有帮助我也不胜欢喜;如果发现有错误的地方也希望能够分享指出,一起加油
客户内部系统,一般不对外开放,因此不适合将文件(如图片)存放到云上,所以存放在本地服务器中
每个(组)截图会配上本人的理解和分析,有不对或不全的地方万望指出
登录后任意界面点击右上角头像位置选择进入个人中心,选择点击头像位置弹出该窗口,选择本地图片上传;
前端调用接口完成头像更新,在数据库的体现为头像字段内容由空转为图片保存地址
存取地址默认会将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包类和方法进行文件的保存
———————
后面还有两个重载的方法,实际上并不进行什么处理,只是为了应对其他使用场景(如不传)存储文件夹、扩展名等……最终都是调用上述方法进行存储操作
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
若依文件工具类调用该类的方法进行文件的读写
该方法为上一张图中的 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);
}