需求原因
项目系统随着使用时长的增加,存储的文件数量也会逐渐增长,为了方便更好地对文件(附件)进行统一的管理,需要将文件保存路径存放到一张附件表中,获取有附件的主表数据是调用查询附加表获取到文件信息
另外,有的主表数据可能有不只一个附件,都放在数据字段中可能影响性能和后续维护成本
要求
从配置表中读取到根目录信息,不同业务文件分别保存在不同文件;
文件主要信息有:文件路径、文件名、文件大小、文件类型、上传时间、调用文件的主表id等
思路
界面选择文件进行上传——>由配置表、当前业务、当前日期时间等统一命名目录名、文件名等~~(可能后续会更改)~~
——>保存文件——>保存文件信息到s308表——>返回执行结果
若依的文件上传
https://blog.csdn.net/weixin_48415369/article/details/126098059
个人感觉的话若依框架中已经编写好的文件上传功能已经很完善了,对于根目录的配置方式也很方便(几遍打成jar包yml文件也不会被编译,可以直接修改或覆盖),但是毕竟需求有出入,也只能进行修改,当然目前修改的情况只能说可用,还算不上完善,根据后续功能的开发也将逐渐完善本文所言的文件上传功能
实现
控制器
@Autowired
private SysConfigMapper configMapper;
@Autowired
private Sys308Service sys308Service;
@PostMapping("/avatar")
public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception
{
// 判断头像文件不为空
if (!file.isEmpty())
{
// 通过Security获取到登录用户信息
LoginUser loginUser = getLoginUser();
// 拼接根路径
// 从配置表获取到根目录信息
SysConfig sysConfig = configMapper.checkConfigKeyUnique("sys.file.basePath");
// 进入到头像文件夹
String basePath = sysConfig.getConfigValue() + "/avatar/";
String absPath = sys308Service.fileUpload(basePath, file, loginUser.getUserId());
if (absPath == null) {
return AjaxResult.error("上传图片异常,请联系管理员");
}
// 保存到数据库——>通过用户名查找到对应用户修改头像地址信息
if (userService.updateUserAvatar(loginUser.getUsername(), absPath))
{
AjaxResult ajax = AjaxResult.success();
// 返回值中增加头像地址(为空时默认使用若依头像)
ajax.put("imgUrl", absPath);
// 更新缓存用户头像
loginUser.getUser().setAvatar(absPath);
// 更新token
tokenService.setLoginUser(loginUser);
// 返回前端
return ajax;
}
}
return AjaxResult.error("上传图片异常,请联系管理员");
}
实现类
@Autowired
private Sys308Mapper sys308Mapper;
public String fileUpload(String basePath, MultipartFile file, String idMain) {
try {
// 头像转为输入流后续读出
InputStream is = file.getInputStream();
// 创建filedirStr保存存储头像的文件夹层级——>当前还是包含文件的
String filedirStr = basePath + extractFilename(file);
// 创建absPath保存文件全路径
String absPath = filedirStr;
// 分隔全路径,用来截取出文件夹层级
String[] split = filedirStr.split("/");
int i = split[split.length - 1].indexOf(".");
// 获取到文件名
String fileName = split[split.length - 1].substring(0, i);
// 通过获取文件名索引来截取到文件夹层级
filedirStr = filedirStr.substring(0, filedirStr.indexOf(fileName));
// 生成文件夹对象
File filedir = new File(filedirStr);
// 不存在就创建所有层级文件夹
if (!filedir.exists()) {
filedir.mkdirs();
}
// 文件对象
File outFile = new File(absPath);
// 文件对象转为输出流
OutputStream os = new FileOutputStream(outFile);
// 读出头像文件数据并写入到本地头像文件
byte[] byteStr = new byte[1024];
int len = 0;
while ((len = is.read(byteStr)) > 0) {
os.write(byteStr, 0, len);
}
// 关闭输入流
is.close();
// 输出文件数据
os.flush();
// 关闭输出流
os.close();
// 创建Sys308对象,设置信息
Sys308 sys308 = new Sys308();
// 地址
sys308.setS308VarFilePath(absPath);
// 文件名
sys308.setS308VarFileName(fileName);
// 大小
sys308.setS308IntSize(Integer.parseInt(file.getSize() + ""));
// 类型
// 通过文件名长度截取文件类型
sys308.setS308VarFileType(split[split.length - 1].substring(fileName.length()) + 1);
// 原表
sys308.setS308IdTable("sys_user");
// 原表id
sys308.setS308IdMain(idMain);
// 保存到附件表s308
sys308Mapper.saveS308(sys308);
return absPath;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
实现类方法:传入根目录、文件、
主表id返回保存后的全路径
思路:流转换
将传入文件转为输入流,将其中内容读取到将要生成的文件的输出流,
迭代方式读取完后flush出文件到对应位置
收获
其实不管是什么功能的修改或编写,如果有过编写的经验实际开发难度也并不会太大,事实上这里的文件上传功能也并不涉及到什么比较绕或者复杂的业务逻辑(也可能跟目前对于整个项目的了解不全面有关);
虽然之前也有过文件上传相关的功能开发,但今天在进行文件上传改写的过程中也还是出现了不少的问题:
比如希望跟若依一样通过yml文件来配置根目录地址,这样也可能以根据不同的业务动态选择不同的根目录来保存文件,但不知道问题出在哪里我从yml文件读到的数据总是null,更没法进行调用什么得了……还是经验问题吧可能(手动捂脸);
又比如希望能够在若依原有的实现逻辑上进行修改,减少开发成本嘛,但是!一旦涉及到若依自带的实现流程,就很难保证保存到数据库中的地址是我所希望的那样——全路径了,若依自身是使用了别名(我的理解)的方式代替根路径,这样就导致结果并不是我所期望的
很明显出了不少问题,不过还好最终也都是解决了;
虽然但是,花费了不少时间最终还是将需求给实现了,目前只是调用了一个 extractFilename(file) 去生成年月日格式的文件夹层级和文件名,这也极大程度上减少了开发时的困扰——怎么处理将来可能存在的条件查询;
解决办法是一个个搜一个个试出来的,多多少少也算是增长了一些经验,也复习了一下文件流的使用