> 技术文档 > 若依框架篇-若依集成 X-File-Storage 框架(实现图片上传阿里云 OSS 服务器)、EasyExcel 框架(实现 Excel 数据批量导入功能)_若依导入excel

若依框架篇-若依集成 X-File-Storage 框架(实现图片上传阿里云 OSS 服务器)、EasyExcel 框架(实现 Excel 数据批量导入功能)_若依导入excel


🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 实现使用 Excel 文件批量导入

        1.1 导入功能的前端具体实现

        1.2 导入功能的后端具体实现

        1.3 使用 EasyExcel 框架实现 Excel 读、写功能

        1.4 将 Easy Excel 集成到若依框架中

        2.0 阿里云 OSS 概述

        2.1 使用阿里云 OSS 具体过程

        2.2 将 X-File-Storage 集成到若依框架中


        1.0 实现使用 Excel 文件批量导入

        使用若依框架实现了 CRUD 之后,若依会自动实现导出功能:

        将当前页面所展示的信息,以 Excel 的形式导入到本地中:

        导出功能若依由若依框架实现的,对于导入功能,若依框架没有实现该功能。

若依框架实现导出功能:

        1)前端代码:

        首先定义了一个按钮,关联着 handleExport 方法:

        2)后端代码:

        在 Controller 层中,调用接口获取到 list 需要导出的数据,通过 ExcelUtil 工具类,调用该工具类的 exportExcel() 方法,来将数据进行导出。

        Sku 是一个根据业务需要生成的一个实体类:

        通过 @Excel 注解来实现实体类与 Excel 表格中的数据进行映射。

        1.1 导入功能的前端具体实现

根据业务需要实现:

        1)首先需要定义一个按钮,点击之后,弹出对话框:

  导入 

        该按钮关联着 handleImport 的方法,当触发点击事件之之后,弹出对话框:

const excelOpen = ref(false);function handleImport(){ excelOpen.value = true;}

        2)对话框的内容:

        根据 Element 官网参考实现文件上传功能:Upload 上传 | Element Plus

         在对话框中插入文件上传的功能:

        使用了 el-dialog 组件来创建一个对话框,用于导入数据。对话框内嵌了一个 el-upload 组件,用于文件上传。

     上传文件   上传   
上传文件仅支持,xls/xlsx格式,文件大小不得超过1M

对应的 JS 代码: 

const uploadRef = ref({});function submitUpload(){ uploadRef.value.submit();}

标签中属性解析:

        - v-model=\"excelOpen\": 控制对话框的显示与隐藏。
        - :action=\"uploadExcelUrl\": 文件上传的目标 URL。
        - :headers=\"headers\": 上传请求的头部信息。
        - :on-success=\"handleUploadSuccess\": 文件上传成功后的回调函数。
        - :on-error=\"handleUploadError\": 文件上传失败后的回调函数。
        - :before-upload=\"handleBeforeUpload\": 文件上传前的钩子函数,可以用来做文件校验。
        - :limit=\"1\": 限制一次只能上传一个文件。
        - :auto-upload=\"false\": 禁用自动上传,需要手动触发上传。

        3)前端实现导入功能的完整代码

        基于若依框架实现的前端代码:

 导入   上传文件   上传   
上传文件仅支持,xls/xlsx格式,文件大小不得超过1M
import { getToken } from \"@/utils/auth\"; /* 打开数据导入对话框 */const excelOpen = ref(false);function handleExcelImport() { excelOpen.value = true;}/* 上传地址 */const uploadExcelUrl = ref(import.meta.env.VITE_APP_BASE_API + \"/manage/sku/import\"); // 上传excel文件地址/* 上传请求头 */const headers = ref({ Authorization: \"Bearer \" + getToken() });/* 上传excel */const uploadRef = ref({});function submitUpload() { uploadRef.value.submit()} const props = defineProps({ modelValue: [String, Object, Array], // 大小限制(MB) fileSize: { type: Number, default: 1, }, // 文件类型, 例如[\"xls\", \"xlsx\"] fileType: { type: Array, default: () => [\"xls\", \"xlsx\"], },});// 上传前loading加载function handleBeforeUpload(file) { let isExcel = false; if (props.fileType.length) { let fileExtension = \"\"; if (file.name.lastIndexOf(\".\") > -1) { fileExtension = file.name.slice(file.name.lastIndexOf(\".\") + 1); } isExcel = props.fileType.some(type => { if (file.type.indexOf(type) > -1) return true; if (fileExtension && fileExtension.indexOf(type) > -1) return true; return false; }); } if (!isExcel) { proxy.$modal.msgError( `文件格式不正确, 请上传${props.fileType.join(\"/\")}格式文件!` ); return false; } if (props.fileSize) { const isLt = file.size / 1024 / 1024 < props.fileSize; if (!isLt) { proxy.$modal.msgError(`上传excel大小不能超过 ${props.fileSize} MB!`); return false; } } proxy.$modal.loading(\"正在上传excel,请稍候...\");}// 上传失败function handleUploadError() { proxy.$modal.msgError(\"上传excel失败\"); uploadRef.value.clearFiles(); proxy.$modal.closeLoading();}// 上传成功回调function handleUploadSuccess(res, file) { if (res.code === 200) { proxy.$modal.msgSuccess(\"上传excel成功\"); excelOpen.value = false; getList(); }else{ proxy.$modal.msgError(\"res.msg\"); } uploadRef.value.clearFiles(); proxy.$modal.closeLoading();}

        1.2 导入功能的后端具体实现

        与导出 Excel 功能一样,创建了 ExcelUtil 对象,使用该对象的 importExcel() 方法,将文件流进行导入,返回的结果是一个 List 集合数据,接下来可以进行批量插入数据了。

        1.3 使用 EasyExcel 框架实现 Excel 读、写功能

        EasyExcel 官网:EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网

        阿里巴巴开源的框架,它以使用简单、功能强大和节省内存而著称,特别适合于需要进行大量数据导入和导出的场景。

        Java 解析、生成 Excel 比较有名的框架有 Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi 有一套 SAX 模式的 API 可以一定程度的解决一些内存溢出的问题,但 POI 还是有一些缺陷,比如 07 版 Excel 解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。 easyexcel 重写了 poi 对 07 版 Excel 的解析,一个 3M 的 excel 用 POI sax 解析依然需要 100M 左右内存,改用 easyexcel 可以降低到 几M,并且再大的 excel 也不会出现内存溢出;03 版依赖 POI 的 sax 模式,在上层做了模型转换的封装,让使用者更加简单方便。

        1.4 将 Easy Excel 集成到若依框架中

        1)dkd-common\\pom.xml 模块添加整合依赖:

 com.alibaba easyexcel 4.0.1

        2)在 dkd-common\\ 模块的 ExcelUtil.java 新增 easyexcel 导出导入方法:

/** * 对excel表单默认第一个索引名转换成list(EasyExcel) * * @param is 输入流 * @return 转换后集合 */public List importEasyExcel(InputStream is) throws Exception{return EasyExcel.read(is).head(clazz).sheet().doReadSync();}/** * 对list数据源将其里面的数据导入到excel表单(EasyExcel) * * @param list 导出数据集合 * @param sheetName 工作表的名称 * @return 结果 */public void exportEasyExcel(HttpServletResponse response, List list, String sheetName){try{EasyExcel.write(response.getOutputStream(), clazz).sheet(sheetName).doWrite(list);}catch (IOException e){log.error(\"导出EasyExcel异常{}\", e.getMessage());}}

        3)实体类的属性上添加 @ExcelProperty 注解:

        添加完 @ExcelPropety 注解之后,实现实体类的属性与 Excel 表格字段映射。还需要在实体类上添加注解:

        - @ExcelIgnoreUnannotated// 注解表示在导出Excel时,忽略没有被任何注解标记的字段
        - @ColumnWidth(16)// 注解用于设置列的宽度
        - @HeadRowHeight(14)// 注解用于设置表头行的高度
        - @HeadFontStyle(fontHeightInPoints = 11)// 注解用于设置表头的字体样式

举个例子:

package com.dkd.manage.domain;import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;import com.alibaba.excel.annotation.ExcelProperty;import com.alibaba.excel.annotation.write.style.ColumnWidth;import com.alibaba.excel.annotation.write.style.HeadFontStyle;import com.alibaba.excel.annotation.write.style.HeadRowHeight;import com.dkd.common.annotation.Excel;import com.dkd.common.core.domain.BaseEntity;import org.apache.commons.lang3.builder.ToStringBuilder;import org.apache.commons.lang3.builder.ToStringStyle;/** * 商品管理对象 tb_sku * * @author itheima * @date 2024-07-15 */@ExcelIgnoreUnannotated// 注解表示在导出Excel时,忽略没有被任何注解标记的字段@ColumnWidth(16)// 注解用于设置列的宽度@HeadRowHeight(14)// 注解用于设置表头行的高度@HeadFontStyle(fontHeightInPoints = 11)// 注解用于设置表头的字体样式public class Sku extends BaseEntity{ private static final long serialVersionUID = 1L; /** 主键 */ private Long skuId; /** 商品名称 */ @Excel(name = \"商品名称\") @ExcelProperty(\"商品名称\") private String skuName; /** 商品图片 */ @Excel(name = \"商品图片\") @ExcelProperty(\"商品图片\") private String skuImage; /** 品牌 */ @Excel(name = \"品牌\") @ExcelProperty(\"品牌\") private String brandName; /** 规格(净含量) */ @Excel(name = \"规格(净含量)\") @ExcelProperty(\"规格(净含量)\") private String unit; /** 商品价格 */ @Excel(name = \"商品价格\") @ExcelProperty(\"商品价格\") private Long price; /** 商品类型Id */ @Excel(name = \"商品类型Id\") @ExcelProperty(\"商品类型Id\") private Long classId; /** 是否打折促销 */ private Integer isDiscount;// 其他略...}

        完成之后,就可以使用了,通过创建 ExcelUtil 对象,调用 importEasyExcel() 方法实现导入功能,调用 exportEasyExcel() 方法实现导出功能。

举个例子:

导出结果:

        2.0 阿里云 OSS 概述

        阿里云对象存储 OSS(Object Storage Service),是一款海量、安全、低成本、高可靠的云存储服务。使用 OSS,您可以通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种文件。

        使用了阿里云 OSS 对象存储服务之后,我们的项目当中如果涉及到文件上传这样的业务,在前端进行文件上传并请求到服务端时,在服务器本地磁盘当中就不需要再来存储文件了。我们直接将接收到的文件上传到 oss,由 oss 帮我们存储和管理,同时阿里云的 oss 存储服务还保障了我们所存储内容的安全可靠。

        2.1 使用阿里云 OSS 具体过程

        1)注册阿里云账户(注册完成后需要实名认证)

        2)注册完账号之后,就可以登录阿里云

        3)通过控制台找到对象存储 OSS 服务

        4)如果是第一次访问,还需要开通对象存储服务 OSS

        5)开通 OSS 服务之后,就可以进入到阿里云对象存储的控制台

        6)点击左侧的 \"Bucket列表\",创建一个Bucket

        7)点击 \"AccessKey管理\",进入到管理页面

        8)以管理员身份打开CMD命令行,执行如下命令,配置系统的环境变量

set OSS_ACCESS_KEY_ID=LTAI5tXXXXXXXXXXXXXXXXXXXXM8TPset OSS_ACCESS_KEY_SECRET=UzMcJXXXXXXXXXXXXXXXXXXXXdabTNafi

        将上述的ACCESS_KEY_ID 与 ACCESS_KEY_SECRET 的值一定要替换成自己的 。

        执行如下命令,让更改生效:

setx OSS_ACCESS_KEY_ID \"%OSS_ACCESS_KEY_ID%\"setx OSS_ACCESS_KEY_SECRET \"%OSS_ACCESS_KEY_SECRET%\"

         执行如下命令,验证环境变量是否生效:

echo %OSS_ACCESS_KEY_ID%echo %OSS_ACCESS_KEY_SECRET%

        阿里云oss 对象存储服务的准备工作我们已经完成了,接下来我们就来完成第二步操作:参照官方所提供的sdk示例来编写入门程序。

        1)首先我们需要来打开阿里云OSS的官方文档,在官方文档中找到 SDK 的示例代码:

        2)文档阅读:

        3)参照官方提供的SDK,改造一下,即可实现文件上传功能:

package com.dkd.common.test;import com.aliyun.oss.ClientException;import com.aliyun.oss.OSS;import com.aliyun.oss.common.auth.*;import com.aliyun.oss.OSSClientBuilder;import com.aliyun.oss.OSSException;import com.aliyun.oss.model.PutObjectRequest;import com.aliyun.oss.model.PutObjectResult;import java.io.FileInputStream;import java.io.InputStream;public class Demo { public static void main(String[] args) throws Exception { // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。 String endpoint = \"https://oss-cn-beijing.aliyuncs.com\"; // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider(); // 填写Bucket名称,例如examplebucket。 String bucketName = \"dkd-itheima\"; // 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。 String objectName = \"gao.png\"; // 填写本地文件的完整路径,例如D:\\\\localpath\\\\examplefile.txt。 // 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。 String filePath= \"E:\\\\temp\\\\upload\\\\gao.png\"; // 创建OSSClient实例。 OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider); try { InputStream inputStream = new FileInputStream(filePath); // 创建PutObjectRequest对象。 PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, inputStream); // 创建PutObject请求。 PutObjectResult result = ossClient.putObject(putObjectRequest); } catch (OSSException oe) { System.out.println(\"Caught an OSSException, which means your request made it to OSS, \"  + \"but was rejected with an error response for some reason.\"); System.out.println(\"Error Message:\" + oe.getErrorMessage()); System.out.println(\"Error Code:\" + oe.getErrorCode()); System.out.println(\"Request ID:\" + oe.getRequestId()); System.out.println(\"Host ID:\" + oe.getHostId()); } catch (ClientException ce) { System.out.println(\"Caught an ClientException, which means the client encountered \"  + \"a serious internal problem while trying to communicate with OSS, \"  + \"such as not being able to access the network.\"); System.out.println(\"Error Message:\" + ce.getMessage()); } finally { if (ossClient != null) { ossClient.shutdown(); } } }}

在以上代码中,需要替换的内容为:

        - endpoint:阿里云OSS中的bucket对应的域名

        - bucketName:Bucket名称

        - objectName:对象名称,在Bucket中存储的对象的名称

        - filePath:文件路径

运行以上程序后,会把本地的文件上传到阿里云OSS服务器上。

        2.2 将 X-File-Storage 集成到若依框架中

        相比直接使用阿里云 OSS,使用 X-File-Storage 上传图片到云服务器的操作会更加容易,使用 X-File-Storage 可以简化图片上传到云服务器的过程,特别是对于那些不熟悉阿里云 OSS API 或者希望减少开发工作量的开发者来说。X-File-Storage 是一个抽象层,它封装了底层存储服务的复杂性,提供了更简单、更统一的接口。 

将 X-File-Storage 集成到若依框架中的步骤:

        1)在 dkd-common 的 pom.xml 中引入依赖:

 org.dromara.x-file-storage x-file-storage-spring 2.1.0 com.aliyun.oss aliyun-sdk-oss 3.16.1

        2)在 dkd-admin 的 application.yml 配置文件中先添加以下基础配置,再添加对应平台的配置:

# 文件上传dromara: x-file-storage: #文件存储配置 default-platform: aliyun-oss-1 #默认使用的存储平台 thumbnail-suffix: \".min.jpg\" #缩略图后缀,例如【.min.jpg】【.png】 #对应平台的配置写在这里,注意缩进要对齐 aliyun-oss: - platform: aliyun-oss-1 # 存储平台标识 enable-storage: true # 启用存储 access-key: ?? secret-key: ?? end-point: oss-cn-qingdao.aliyuncs.com bucket-name: itheima-007 domain: https://itheima-007.oss-cn-qingdao.aliyuncs.com/ # 访问域名,注意“/”结尾,例如:https://abc.oss-cn-shanghai.aliyuncs.com/ base-path: dkd-images/ # 基础路径

        3)在 dkd-admin 的启动类上加上 @EnableFileStorage 注解:

@EnableFileStorage@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })public class DkdApplication{ public static void main(String[] args) { // System.setProperty(\"spring.devtools.restart.enabled\", \"false\"); SpringApplication.run(DkdApplication.class, args); System.out.println(\"(♥◠‿◠)ノ゙ 帝可得启动成功 ლ(´ڡ`ლ)゙\"); }}

        4)修改若依默认上传图片代码

        找到 ruoyi-admin 模块中的 com.ruoyi.web.controller.common.CommonController 类,修改单个文件上传的方法:

@Autowiredprivate FileStorageService fileStorageService;//注入实列/** * 通用上传请求(单个)*/@PostMapping(\"/upload\")public AjaxResult uploadFile(MultipartFile file) throws Exception { try { // 指定oss保存文件路径 String objectName = LocalDate.now().format(DateTimeFormatter.ofPattern(\"yyyy/MM/dd\")) + \"/\"; // 上传图片,成功返回文件信息 FileInfo fileInfo = fileStorageService.of(file).setPath(objectName).upload(); // 设置返回结果 AjaxResult ajax = AjaxResult.success(); ajax.put(\"url\", fileInfo.getUrl()); ajax.put(\"fileName\", fileInfo.getUrl()); //注意:这里的值要改为url,前端访问的地址,需要文件的地址 而不是文件名称 ajax.put(\"newFileName\", fileInfo.getUrl()); ajax.put(\"originalFilename\", file.getOriginalFilename()); return ajax; } catch (Exception e) { return AjaxResult.error(e.getMessage()); }}