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

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

文章目录

[1.0 实现使用 Excel 文件批量导入](#1.0 实现使用 Excel 文件批量导入)

[1.1 导入功能的前端具体实现](#1.1 导入功能的前端具体实现)

[1.2 导入功能的后端具体实现](#1.2 导入功能的后端具体实现)

[1.3 使用 EasyExcel 框架实现 Excel 读、写功能](#1.3 使用 EasyExcel 框架实现 Excel 读、写功能)

[1.4 将 Easy Excel 集成到若依框架中](#1.4 将 Easy Excel 集成到若依框架中)

[2.0 阿里云 OSS 概述](#2.0 阿里云 OSS 概述)

[2.1 使用阿里云 OSS 具体过程](#2.1 使用阿里云 OSS 具体过程)

[2.2 将 X-File-Storage 集成到若依框架中](#2.2 将 X-File-Storage 集成到若依框架中)


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

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

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

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

若依框架实现导出功能:

1)前端代码:

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

2)后端代码:

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

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

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

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

根据业务需要实现:

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

html 复制代码
      <el-col :span="1.5">
        <el-button
          type="warning"
          plain
          icon="upload"
          @click="handleImport"
          v-hasPermi="['manage:sku:add']"
        >导入</el-button>
      </el-col>

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

html 复制代码
const excelOpen = ref(false);
function handleImport(){
  excelOpen.value = true;

}

2)对话框的内容:

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

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

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

html 复制代码
    <!-- 数据导入对话框 -->
    <el-dialog title="导入数据" v-model="excelOpen" width="400px" append-to-body>
        <el-upload
          ref="uploadRef"
          class="upload-demo"
          :action="uploadExcelUrl"
          :headers="headers"
          :on-success="handleUploadSuccess"
          :on-error="handleUploadError"
          :before-upload="handleBeforeUpload"
          :limit="1"
          :auto-upload="false"
            >
          <template #trigger>
            <el-button type="primary">上传文件</el-button>
          </template>

          <el-button class="ml-3" type="success" @click="submitUpload">
            上传
          </el-button>

          <template #tip>
            <div class="el-upload__tip">
              上传文件仅支持,xls/xlsx格式,文件大小不得超过1M
            </div>
          </template>
        </el-upload>
    </el-dialog>

对应的 JS 代码:

html 复制代码
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)前端实现导入功能的完整代码

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

html 复制代码
<!--  导入按钮-->
<el-col :span="1.5">
    <el-button type="warning" plain icon="Upload" @click="handleExcelImport" v-hasPermi="['manage:sku:add']">导入</el-button>
</el-col>

<!-- 数据导入对话框 -->
<el-dialog title="数据导入" v-model="excelOpen" width="400px" append-to-body>
  <el-upload ref="uploadRef" class="upload-demo"
    :action="uploadExcelUrl"
    :headers="headers"
    :on-success="handleUploadSuccess"
    :on-error="handleUploadError"
    :before-upload="handleBeforeUpload"         
    :limit="1"
    :auto-upload="false">
    <template #trigger>
      <el-button type="primary">上传文件</el-button>
    </template>
    <el-button class="ml-3" type="success" @click="submitUpload">
      上传
    </el-button>
    <template #tip>
      <div class="el-upload__tip">
        上传文件仅支持,xls/xlsx格式,文件大小不得超过1M
      </div>
    </template>
  </el-upload>
</el-dialog>

<script setup name="Sku">
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();
}    
</script>

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 模块添加整合依赖:

XML 复制代码
<!--  excel处理工具-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>4.0.1</version>
</dependency>

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

java 复制代码
/**
 * 对excel表单默认第一个索引名转换成list(EasyExcel)
 * 
 * @param is 输入流
 * @return 转换后集合
 */
public List<T> 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<T> 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)// 注解用于设置表头的字体样式**

举个例子:

java 复制代码
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命令行,执行如下命令,配置系统的环境变量

java 复制代码
set OSS_ACCESS_KEY_ID=LTAI5tXXXXXXXXXXXXXXXXXXXXM8TP
set OSS_ACCESS_KEY_SECRET=UzMcJXXXXXXXXXXXXXXXXXXXXdabTNafi

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

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

java 复制代码
setx OSS_ACCESS_KEY_ID "%OSS_ACCESS_KEY_ID%"
setx OSS_ACCESS_KEY_SECRET "%OSS_ACCESS_KEY_SECRET%"

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

java 复制代码
echo %OSS_ACCESS_KEY_ID%
echo %OSS_ACCESS_KEY_SECRET%

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

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

2)文档阅读:


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

java 复制代码
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 中引入依赖:

XML 复制代码
<!-- 文件上传-->
<dependency>
    <groupId>org.dromara.x-file-storage</groupId>
    <artifactId>x-file-storage-spring</artifactId>
    <version>2.1.0</version>
</dependency>
<!-- 阿里云oss-->
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.16.1</version>
</dependency>

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

XML 复制代码
# 文件上传
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 注解:

java 复制代码
@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 类,修改单个文件上传的方法:

java 复制代码
@Autowired
private 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());
    }
}
相关推荐
AIGC绘画5 分钟前
Spring微服务概述之spring cloud alibaba服务调用实践
java·spring·微服务
wwangxu6 分钟前
Java 面向对象基础
java·开发语言
uzong23 分钟前
JDK高性能套路: 自旋(for(;;)) + CAS
java·后端
幽兰的天空35 分钟前
python实现excel数据导入数据库
数据库
带刺的坐椅1 小时前
Spring SPI、Solon SPI 有点儿像(Maven 与 Gradle)
java·spring·solon·spi
冷眼看人间恩怨1 小时前
【Java】揭秘网络编程:深入探索其无尽奥秘与魅力
java·开发语言·tcp/ip·udp·tcp
武昌库里写JAVA2 小时前
【MySql】-0.1、Unbunt20.04二进制方式安装Mysql5.7和8.0
spring boot·spring·毕业设计·layui·课程设计
Algorithm15762 小时前
JVM是什么,与Java的关系是什么,以及JVM怎么实现的跨平台性
java·开发语言·jvm
尘佑不尘2 小时前
shodan5,参数使用,批量查找Mongodb未授权登录,jenkins批量挖掘
数据库·笔记·mongodb·web安全·jenkins·1024程序员节
传输能手2 小时前
从三方云服务器将数据迁移至本地,如何保障安全高效?
大数据·服务器·数据库