一、写在开头

今天终于更新新专栏 《EfficientFarm》 的第二篇博文啦,本文主要来记录一下对于EasyExcel的高效应用,包括对MySQL数据库百万级数据量的导入与导出操作,以及性能的优化(争取做到秒级性能!)。


二、如何做技术选型

其实在市面上我们有很多常用的excel操作依赖库,除了EasyExcel之外,还有EasyPOI、JXL、JXLS等等,他们各有千秋,依赖重点不同,我们在做技术选型的时候,要根据自己的需求去做针对性选择,下面我们列举了这几种常见技术的特点对比

技术方案 优点 缺点
EasyExcel 简单易用,API设计友好;
高效处理大量数据;
支持自定义样式和格式化器等功能
不支持老版本 Excel 文件 (如 xls 格式)
POI Apache开源项目,稳定性高,EasyPOI基于它开发的,特点类似,进行了功能增强,这里不单独列举;
支持多种格式(XLS、XLSX等);
可以读写复杂表格(如带有合并单元格或图表的表格)
API使用较为繁琐;对于大数据量可能会存在性能问题
Jxls 具备良好的模板引擎机制,支持通过模板文件生成 Excel 表格;
提供了可视化设计器来快速创建报告模板
性能相对其他两个方案稍弱一些;
模板与代码耦合度较高。

而本文中主要针对的是大数据量的导入与导出,因此,我们果断的选择了EasyExcel技术进行实现。


三、应用场景模拟

假设我们在开发中接到了一个需求要求我们做一个功能:

1、导出商城中所有的用户信息,由于用户规模达到了百万级,导出等待时间不可太长

2、允许通过规定的excel模板进行百万级用户信息的初始化(系统迁移时会发生)。

拿到这个需求后,经过技术选型EasyExcel后,我们在心里有个大概的构想了,大概可以分三个内容 :“模板下载”、“上传数据”、“下载数据”

想好这些后,我们就可以开整了!


四、数据准备

在数据准备阶段,我们应该做如下几点:

1. 在数据库中创建一个用户信息表User;

-- 如果存在表先删除
drop table if exists `user`;
--建表语句
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '员工姓名',
`phone_num` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '联系方式',
`address` varchar(200) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '住址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

2. 准备一个用户信息导入的初始化模板;

3. 模拟创造百万数据量在User表中;

这一点其实有2种方案,第一种就是在创造好的模板文件xlsx中,手工造出100万的数据,xlsx单个sheet页最大可创建104万行数据,刚刚好满足,如果用xls单个sheet还不可以,这种肯定相对麻烦,并且100万的数据有几十M,打开就已经很慢了;

另外一种方案,可以通过存储过程向MySQL中加入100w条数据,不过性能也不好,毕竟数据量太大,自己斟酌吧,sql贴出来(性能不好的电脑,不建议这么干,容易把软件跑崩):

DELIMITER //
drop procedure IF EXISTS InsertTestData;
CREATE PROCEDURE InsertTestData()
BEGIN
DECLARE counter INT DEFAULT 1; WHILE counter < 1000000 DO
INSERT INTO user (id, name, phone_num, address) VALUES
(counter, CONCAT('name_', counter), CONCAT('phone_', counter), CONCAT('add_',counter)) ;
SET counter = counter + 1;
END WHILE;
END //
DELIMITER; -- 调用存储过程插入数据
CALL InsertTestData();

五、SpringBoot中配置EasyExcel

5.1 pom.xml中引入依赖

本次代码中一共用到了如下这些依赖,很多小伙伴本地若已经引入了,可以忽略!

<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--MyBatis Plus依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<!--easyexcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.4</version>
</dependency>
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.25</version>
</dependency>

5.2 创建实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@ColumnWidth(25)
public class User {
/**
* 主键
*
* @mbg.generated
*/
@ExcelProperty("id")
private Integer id; /**
* 员工姓名
*
* @mbg.generated
*/
@ExcelProperty("姓名")
private String name; /**
* 联系方式
*
* @mbg.generated
*/
@ExcelProperty("联系方式")
private String phoneNum; /**
* 住址
*
* @mbg.generated
*/
@ExcelProperty("联系地址")
private String address; }

【注解说明】

  • @ExcelProperty:声明列名。
  • @ColumnWidth:设置列宽。也可以直接作用在类上。统一每一列的宽度

5.3 创建数据关系映射

UserMapper 文件

//*注:这里面继承了mybatis-plus的BaseMapper接口,供后面进行分页查询使用。*
public interface UserMapper extends BaseMapper<User> {
int deleteByPrimaryKey(Integer id); int insertAll(User record); void insertSelective(@Param("list") List<User> list); User selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(User record); int updateByPrimaryKey(User record);
Integer countNum();
}

UserMapper .xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.javaboy.vhr.mapper.UserMapper">
<resultMap id="BaseResultMap" type="org.javaboy.vhr.pojo.User">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="phone_num" jdbcType="VARCHAR" property="phoneNum" />
<result column="address" jdbcType="VARCHAR" property="address" />
</resultMap>
<sql id="Base_Column_List">
id, name, phone_num, address
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from user
where id = #{id,jdbcType=INTEGER}
</select>
<select id="countNum" resultType="java.lang.Integer">
select count(*) from user
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from user
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insertAll" keyColumn="id" keyProperty="id" parameterType="org.javaboy.vhr.pojo.User" useGeneratedKeys="true">
insert into user (name, phone_num, address
)
values (#{name,jdbcType=VARCHAR}, #{phoneNum,jdbcType=VARCHAR}, #{address,jdbcType=VARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="org.javaboy.vhr.pojo.User">
insert into user
(id,name, phone_num, address
)
values
<foreach collection="list" item="item" separator=",">
(#{item.id},#{item.name},#{item.phoneNum},#{item.address})
</foreach>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="org.javaboy.vhr.pojo.User">
update user
<set>
<if test="name != null">
name = #{name,jdbcType=VARCHAR},
</if>
<if test="phoneNum != null">
phone_num = #{phoneNum,jdbcType=VARCHAR},
</if>
<if test="address != null">
address = #{address,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="org.javaboy.vhr.pojo.User">
update user
set name = #{name,jdbcType=VARCHAR},
phone_num = #{phoneNum,jdbcType=VARCHAR},
address = #{address,jdbcType=VARCHAR}
where id = #{id,jdbcType=INTEGER}
</update>
</mapper>

六、前端设计

前端页面采用Vue框架实现,咱们就按照上文中构想的那三点来设计就行,可以简单点实现,如果想要更加炫酷的前端样式,比如导入的文件格式校验,数据量提示等等,可以自行网上学习哈。

<template>
<el-card>
<div>
<!--导入数据-->
<el-upload
:show-file-list="false"
:before-upload="beforeUpload"
:on-success="onSuccess"
:on-error="onError"
:disabled="importDataDisabled"
style="display: inline-flex;margin-right: 8px"
action="/employee/excel/import">
<!--导入数据-->
<el-button :disabled="importDataDisabled" type="success" :icon="importDataBtnIcon">
{{importDataBtnText}}
</el-button>
</el-upload>
<el-button type="success" @click="exportEasyExcel" icon="el-icon-download">
导出数据
</el-button>
<el-button type="success" @click="exportExcelTemplate" icon="el-icon-download">
导出模板
</el-button>
</div>
</el-card> </template> <script>
import {Message} from 'element-ui';
export default {
name: "Export",
data() {
return {
importDataBtnText: '导入数据',
importDataBtnIcon: 'el-icon-upload2',
importDataDisabled: false,
}
},
methods: {
onError(res) {
this.importDataBtnText = '导入数据';
this.importDataBtnIcon = 'el-icon-upload2';
this.importDataDisabled = false;
console.log(res);
},
onSuccess(res) {
this.importDataBtnText = '导入数据';
this.importDataBtnIcon = 'el-icon-upload2';
this.importDataDisabled = false;
console.log(res.msg);
if (res.msg == '文件导入成功'){
Message.success("文件导入完成")
}
// this.initEmps(); },
beforeUpload() {
this.importDataBtnText = '正在导入';
this.importDataBtnIcon = 'el-icon-loading';
this.importDataDisabled = true;
},
exportEasyExcel() {
window.open('/employee/excel/easyexcelexport', '_parent');
},
exportExcelTemplate(){
window.open('/employee/excel/exporttemplate', '_parent');
}
}
} </script> <style scoped> </style>

效果如下:


七、导入导出实现

7.1 模板下载

1️⃣ 将准备好的用户信息模板.xlsx文件放入resource对应路径下。

2️⃣ 构建一个控制器类,用以接收导出模板、导入数据、导出数据的请求。

@RestController
@RequestMapping("/employee/excel")
@AllArgsConstructor
@Slf4j
public class EasyExcellController {
/**
* 下载用户信息模板
* @param response
*/
@RequestMapping("/exporttemplate")
public void downloadTemplate(HttpServletResponse response){
try {
//设置文件名
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("template/用户信息模板.xlsx");
//设置头文件,注意文件名若为中文,使用encode进行处理
response.setHeader("Content-disposition", "attachment;fileName=" + java.net.URLEncoder.encode("用户信息模板.xlsx", "UTF-8"));
//设置文件传输类型与编码
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
OutputStream outputStream = response.getOutputStream();
byte[] bytes = new byte[2048];
int len;
while((len = inputStream.read(bytes)) != -1){
outputStream.write(bytes,0,len);
}
outputStream.flush();
outputStream.close();
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
} }

这部分代码中需要注意的是,如果你的模板是中文名字,需要加上java.net.URLEncoder.encode("用户信息模板.xlsx", "UTF-8")解决乱码问题。

7.2 导入数据

1️⃣ 在EasyExcellController类中增加导入数据的请求处理方法;

	@Autowired
EasyExcelServiceImpl easyExcel; /**
* 导入百万excel文件
* @param file
* @return
*/
@RequestMapping("/import")
public RespBean easyExcelImport(MultipartFile file){
if(file.isEmpty()){
return RespBean.error("文件不可为空");
}
easyExcel.easyExcelImport(file);
return RespBean.ok("文件导入成功");
}

代码中的RespBean是自己定义的一个响应工具类。

public class RespBean {
private Integer status;
private String msg;
private Object obj; public static RespBean build() {
return new RespBean();
} public static RespBean ok(String msg) {
return new RespBean(200, msg, null);
} public static RespBean ok(String msg, Object obj) {
return new RespBean(200, msg, obj);
} public static RespBean error(String msg) {
return new RespBean(500, msg, null);
} public static RespBean error(String msg, Object obj) {
return new RespBean(500, msg, obj);
} private RespBean() {
} private RespBean(Integer status, String msg, Object obj) {
this.status = status;
this.msg = msg;
this.obj = obj;
} public Integer getStatus() {
return status;
} public RespBean setStatus(Integer status) {
this.status = status;
return this;
} public String getMsg() {
return msg;
} public RespBean setMsg(String msg) {
this.msg = msg;
return this;
} public Object getObj() {
return obj;
} public RespBean setObj(Object obj) {
this.obj = obj;
return this;
}
}

2️⃣ 在控制器中引入的easyExcel.easyExcelImport(file)方法中进行导入逻辑的实现。

@Service
@Slf4j
@AllArgsConstructor
public class EasyExcelServiceImpl implements EasyExcelService { private final ApplicationContext applicationContext;
/**
* excle文件导入实现
* @param file
*/
@Override
public void easyExcelImport(MultipartFile file) {
try {
long beginTime = System.currentTimeMillis();
//加载文件读取监听器
EasyExcelImportHandler listener = applicationContext.getBean(EasyExcelImportHandler.class);
//easyexcel的read方法进行数据读取
EasyExcel.read(file.getInputStream(), User.class,listener).sheet().doRead();
log.info("读取文件耗时:{}秒",(System.currentTimeMillis() - beginTime)/1000);
} catch (IOException e) {
log.error("导入异常", e.getMessage(), e);
}
}
}

这部分代码的核心是文件读取监听器:EasyExcelImportHandler。

3️⃣ 构建文件读取监听器

@Slf4j
@Service
public class EasyExcelImportHandler implements ReadListener<User> {
/*成功数据*/
private final CopyOnWriteArrayList<User> successList = new CopyOnWriteArrayList<>();
/*单次处理条数*/
private final static int BATCH_COUNT = 20000;
@Resource
private ThreadPoolExecutor threadPoolExecutor;
@Resource
private UserMapper userMapper; @Override
public void invoke(User user, AnalysisContext analysisContext) {
if(StringUtils.isNotBlank(user.getName())){
successList.add(user);
return;
}
if(successList.size() >= BATCH_COUNT){
log.info("读取数据:{}", successList.size());
saveData();
} } /**
* 采用多线程读取数据
*/
private void saveData() {
List<List<User>> lists = ListUtil.split(successList, 20000);
CountDownLatch countDownLatch = new CountDownLatch(lists.size());
for (List<User> list : lists) {
threadPoolExecutor.execute(()->{
try {
userMapper.insertSelective(list.stream().map(o -> {
User user = new User();
user.setName(o.getName());
user.setId(o.getId());
user.setPhoneNum(o.getPhoneNum());
user.setAddress(o.getAddress());
return user;
}).collect(Collectors.toList()));
} catch (Exception e) {
log.error("启动线程失败,e:{}", e.getMessage(), e);
} finally {
//执行完一个线程减1,直到执行完
countDownLatch.countDown();
}
});
}
// 等待所有线程执行完
try {
countDownLatch.await();
} catch (Exception e) {
log.error("等待所有线程执行完异常,e:{}", e.getMessage(), e);
}
// 提前将不再使用的集合清空,释放资源
successList.clear();
lists.clear();
} /**
* 所有数据读取完成之后调用
* @param analysisContext
*/
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
//读取剩余数据
if(CollectionUtils.isNotEmpty(successList)){
log.info("读取数据:{}条",successList.size());
saveData();
}
}
}

在这部分代码中我们需要注意两个问题,第一个是多线程,第二个是EasyExcel提供的ReadListener监听器。

第一个,由于我们在代码里采用了多线程导入,因此我们需要配置一个合理的线程池,以提高导入效率。

@Configuration
public class EasyExcelThreadPoolExecutor { @Bean(name = "threadPoolExecutor")
public ThreadPoolExecutor easyExcelStudentImportThreadPool() {
// 系统可用处理器的虚拟机数量
int processors = Runtime.getRuntime().availableProcessors();
return new ThreadPoolExecutor(processors + 1,
processors * 2 + 1,
10 * 60,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000000));
}
}

第二个,对于ReadListener,我们需要搞清楚它提供的方法的作用。

  • invoke():读取表格内容,每一条数据解析都会来调用;
  • doAfterAllAnalysed():所有数据解析完成了调用;
  • invokeHead() :读取标题,里面实现在读完标题后会回调,本篇文章中未使用到;
  • onException():转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行,本篇文章中未使用到。

4️⃣ 导入100万数据量耗时测试

在做导入测试前,由于100万数据量的excel文件很大,所以我们要在application.yml文件中进行最大可上传文件的配置:

spring:
servlet:
multipart:
max-file-size: 128MB # 设置单个文件最大大小为10MB
max-request-size: 128MB # 设置多个文件大小为100MB

对100万数据进行多次导入测试,所损耗时间大概在500秒左右,8分多钟,这对于我们来说肯定无法接受,所以我们在后面针对这种导入进行彻底优化!

7.3 导出数据

1️⃣ 在EasyExcellController类中增加导出数据的请求处理方法;

/**
* 导出百万excel文件
* @param response
*/
@RequestMapping("/easyexcelexport")
public void easyExcelExport(HttpServletResponse response){
try {
//设置内容类型
response.setContentType("text/csv");
//设置响应编码
response.setCharacterEncoding("utf-8");
//设置文件名的编码格式,防止文件名乱码
String fileName = URLEncoder.encode("用户信息", "UTF-8");
//固定写法,设置响应头
response.setHeader("Content-disposition", "attachment;filename="+ fileName + ".xlsx");
Integer total = userMapper.countNum();
if (total == 0) {
log.info("查询无数据");
return;
}
//指定用哪个class进行写出
ExcelWriter build = EasyExcel.write(response.getOutputStream(), User.class).build();
//设置一个sheet页存储所有导出数据
WriteSheet writeSheet = EasyExcel.writerSheet("sheet").build();
long pageSize = 10000;
long pages = total / pageSize;
long startTime = System.currentTimeMillis();
//数据量只有一页时直接写出
if(pages < 1){
List<User> users = userMapper.selectList(null);
build.write(users, writeSheet);
}
//大数据量时,进行分页查询写入
for (int i = 0; i <= pages; i++) {
Page<User> page = new Page<>();
page.setCurrent(i + 1);
page.setSize(pageSize);
Page<User> userPage = userMapper.selectPage(page, null);
build.write(userPage.getRecords(), writeSheet);
}
build.finish();
log.info("导出耗时/ms:"+(System.currentTimeMillis()-startTime)+",导出数据总条数:"+total);
} catch (Exception e) {
log.error("easyExcel导出失败,e:{}",e.getMessage(),e);
}
}

由于数据量比较大,我们在这里采用分页查询,写入到一个sheet中,如果导出到xls格式的文件中,需要写入到多个sheet中,这种可能会慢一点。

且在Mybatis-Plus中使用分页的话,需要增加一个分页插件的配置

@Configuration
public class MybatisPlusPageConfig {
/**
* 新版分页插件配置
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}

2️⃣ 百万数据量导出测试

经过多次测试发现,100万数据量平均导出耗时在40秒左右,在可以接受的范围内!

八、总结

以上就是SpringBoot项目下,通过阿里开源的EasyExcel技术进行百万级数据的导入与导出,不过针对百万数据量的导入,时间在分钟级别,这很明显不够优秀,但考虑到本文的篇幅已经很长了,我们在下一篇文章针对导入进行性能优化,敬请期待!

九、结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

EasyExcel处理Mysql百万数据的导入导出案例,秒级效率,拿来即用!的更多相关文章

  1. 【EXPDP/IMPDP】ORACLE数据泵导入导出案例(expdp & impdp)

    概要: 因项目需要,通常需要将生产库下的部分数据抽取并恢复到测试库上 本文主要介绍数据泵导入导出的几种情况以及错误处理 案例环境: rhel-server-6.5-x86_64 oracle 11.2 ...

  2. 使用MySQL的SELECT INTO OUTFILE ,Load data file,Mysql 大量数据快速导入导出

    使用MySQL的SELECT INTO OUTFILE .Load data file LOAD DATA INFILE语句从一个文本文件中以很高的速度读入一个表中.当用户一前一后地使用SELECT ...

  3. MySQL 表数据的导入导出

    数据导出 1.  使用 SELECT ...INTO OUTFILE ...命令来导出数据,具体语法如下. mysql> SELECT * FROM tablename INTO OUTFILE ...

  4. mysql 数据到 导入导出 总结

    数据库数据的导入和导出受secure_file_priv配置项影响#限制导入导出,null时无法进行数据的导入导出,空时不限制,设置了目录则只能对该目录下的文件进行导入导出show variables ...

  5. mysql source、mysqldump 导入导出数据(转)

    解决了mysql gbk编码的导入导出问题,感谢作者. 一.导入数据 1.确定 数据库默认编码,比如编码 为gbk,将读入途径编码同样设为gbk,命令为:           set names gb ...

  6. Mysql 大数据量导入程序

    Mysql 大数据量导入程序<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" ...

  7. SQL Server中bcp命令的用法以及数据批量导入导出

    原文:SQL Server中bcp命令的用法以及数据批量导入导出 1.bcp命令参数解析 bcp命令有许多参数,下面给出bcp命令参数的简要解析 用法: bcp {dbtable | query} { ...

  8. 循序渐进开发WinForm项目(5)--Excel数据的导入导出操作

    随笔背景:在很多时候,很多入门不久的朋友都会问我:我是从其他语言转到C#开发的,有没有一些基础性的资料给我们学习学习呢,你的框架感觉一下太大了,希望有个循序渐进的教程或者视频来学习就好了. 其实也许我 ...

  9. Oracle 数据泵导入导出总结

    Oracle 数据泵(IMPDP/EXPDP)导入导出总结 Oracle数据泵导入导出是日常工作中常用的基本技术之一,它相对传统的逻辑导入导出要高效,这种特性更适合数据库对象数量巨大的情形,因为我日常 ...

  10. Oracle 12c pdb的数据泵导入导出

    12c推出了可插拔数据库,在一个容器cdb中以多租户的形式同时存在多个数据库pdb.在为pdb做数据泵导入导出时和传统的数据库有少许不同.           1,需要为pdb添加tansnames ...

随机推荐

  1. 上海站报名启动! 2023年开源产业生态大会OpenHarmony生态分论坛

      作为年内开源领域不容错过的科技盛宴,2023年开源产业生态大会将于12月19日在上海盛大开幕.本次活动由上海市经济和信息化委员会.上海市科学技术协会和"科创中国"开源创新联合体 ...

  2. C# 通过ARP技术来观察目标主机数据包

    由于之前写的C# 实现Arp欺诈的文章属于网络攻击,不能够被展示,所以这边我们稍微说一下C#调用ARP包以及查看其他电脑上网数据包的技术,委婉的说一下ARP在局域网之中的应用. 本文章纯属技术讨论,并 ...

  3. MySQL学习路线一条龙

    引言 在当前的IT行业,无论是校园招聘还是社会招聘,MySQL的重要性不言而喻. 面试过程中,MySQL相关的问题经常出现,这不仅因为它是最流行的关系型数据库之一,而且在日常的软件开发中,MySQL的 ...

  4. 实践指南:EdgeOne与HAI的梦幻联动

    在当今快速发展的数字时代,安全和速度已成为网络服务的基石.EdgeOne,作为腾讯云提供的边缘安全加速平台,以其全球部署的节点和强大的安全防护功能,为用户提供了稳定而高效的网络体验.而HAI(Hype ...

  5. HDC 2022精彩继续,多重亮点进来看!

    原文:https://mp.weixin.qq.com/s/YX5vD4cxM8dA4v2ukFooyA,点击链接查看更多技术内容.  

  6. 服务器日志qsnctfwp

    使用 WireShark 打开日志文件 log.pcpng 获取恶意用户下载的文件 方法一:通过对 FTP-DATA 对象导出,可知下载了名为 flag 的文件,通过 save 可获取文件 方法二:通 ...

  7. 推荐一个计算Grad-CAM的Python库

    前言 类激活图CAM(class activation mapping)用于可视化深度学习模型的感兴趣区域,增加了神经网络的可解释性.现在常用Grad-CAM可视化,Grad-CAM基于梯度计算激活图 ...

  8. Java实现控制台购书系统

    "感谢您阅读本篇博客!如果您觉得本文对您有所帮助或启发,请不吝点赞和分享给更多的朋友.您的支持是我持续创作的动力,也欢迎留言交流,让我们一起探讨技术,共同成长!谢谢!" 代码 im ...

  9. 力扣597(MySQL)-好友申请Ⅰ:总体通过率(简单)

    题目: 此表没有主键,它可能包含重复项.该表包含发送请求的用户的 ID ,接受请求的用户的 ID 以及请求的日期. 此表没有主键,它可能包含重复项.该表包含发送请求的用户的 ID ,接受请求的用户的 ...

  10. 阿里云云原生加速器企业硬之城携手阿里云 Serverless 应用引擎(SAE)打造低代码平台

    简介: 作为入选阿里云首期云原生加速器的企业,硬之城此前也获得了阿里云首批产品生态集成认证,通过云原生加速器项目携手阿里云共建更加丰富的云原生产业生态圈,加速云原生落地. 作者 | 陈泽涛(硬之城产品 ...