解决java POI导入Excel超时问题
由于要导入大量数据,后台会耗费很长时间,导致超时。
本项目前端request.js中设定的超时时间为150s.
const service = axios.create({
baseURL: baseUrl,
withCredentials: true,
timeout: 150000
});
我们的做法是:
前端导入Excel向后台发出请求时,后台立即返回信息“后台正在导入,请稍等!”,向redis中存入键isFinished的值为字符串“0”,并且开启一个线程来完成插入大量数据到数据库的工作,当插入完成则将redis中isFinished的值改为字符串“1”。前端收到“后台正在输入,请稍等!”后,此时导入按钮处于禁用状态并且处于加载状态。通过window.setInterval定时轮询,每隔30秒去后台查询ifFinished值,如果为字符串“1”表示上传完毕,此时在弹框“上传完毕!”,此时导入按钮禁用状态和加载状态都取消,并且刷新列表。
前端代码:
<div style="margin-bottom: 10px">
<el-form :inline="true">
<el-form-item label="">
<el-button type="success" icon="el-icon-download" @click="downloadTemplate">下载模板</el-button>
<el-button :loading="importLoading" type="primary" icon="el-icon-upload" @click="handleImport">导入
</el-button>
<el-upload ref="importUpload" :auto-upload="false" :show-file-list="false" :on-change="handleUploadChange"
:disabled="importDisabled" style="display: inline" action="#" class="upload-demo">
<el-button id="uploadButton" style="display: none" slot="trigger" :loading="importLoading" size="small"
type="primary" icon="el-icon-upload">导入</el-button>
</el-upload>
</el-form-item>
<el-form-item label="">
<el-button type="warning" icon="el-icon-lightning" @click="exportExcel">导出</el-button>
</el-form-item>
</el-form>
</div>
handleUploadChange(file) {
if (file.name.lastIndexOf('.') < 0) {
this.$message.error('上传文件只能是xls、xlsx格式!')
return
}
const testMsg = file.name.substring(file.name.lastIndexOf('.') + 1).toLowerCase()
const extensionXLS = testMsg == 'xls'
const extensionXLSX = testMsg == 'xlsx'
if (!extensionXLS && !extensionXLSX) {
this.$message.error('上传文件只能是xls、xlsx格式!')
return
}
const isLt2M = file.size / 1024 / 1024 < 2
if (!isLt2M) {
this.$message.error('上传文件不能超过 2MB!')
return
}
this.importLoading = true
this.importDisabled = true
const data = new FormData()
data.append('file', file.raw)
medicineListApi.importExcel(data).then(response => {
if (response.status == true) {
this.open2(response.msg)
this.getList()
} else {
this.open2(response.msg)
this.importLoading = false
this.importDisabled = false
}
window.setInterval(() => {
setTimeout(this.getStatus(), 0)
}, 30*1000)
}).catch(() => {
this.open2('抱歉,导入失败')
this.importLoading = false
this.importDisabled = false
})
},
open2(str) {
this.$notify({
title: '提示',
message: str,
duration: 0
})
},
// 请求后台获取是否导入完成的状态
getStatus(){
medicineListApi.getStatus().then(response => {
if (response.data == "1") {
this.open2("上传完毕!")
this.importLoading = false
this.importDisabled = false
this.getList()
}
})
}
项目中我们经常需要实现轮询-每隔几秒请求一次接口刷新数据,一般都会使用setInterval,但要注意单纯使用它会导致页面卡死,解释:setInterval不会清除定时器队列,每重复执行1次都会导致定时器叠加,最终卡死你的网页。但是setTimeout是自带清除定时器的
解决办法:
window.setInterval(() => {
setTimeout(fun, 0)
}, 30000)
setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式。 如果你只想重复执行可以使用 setInterval() 方法。setTimeout()只执行一次,而setInterval可以多次调用。
medicineList.js代码:
import request from "./request"; const baseUrl = "/medicineList"
export const medicineListApi = {
/**
* 导入
* @param data
*/
importExcel(data) {
return request({
url: baseUrl + '/import',
method: 'post',
data: data,
headers: {
'Content-Type': 'multipart/form-data'
}
})
},
getStatus() {
return request({
url: baseUrl + "/getStatus",
method: "GET"
});
},
};
后台代码:
controller:
@RestController
@RequestMapping("/medicineList")
@Slf4j
public class MedicineListController extends BaseController { @Autowired
private MedicineListService medicineListService; /**
* 导入药品信息
*
* @param file
* @return
* @throws Exception
*/
@PostMapping("/import")
public JSONObject importNodeInfo(MultipartFile file) throws Exception {
return medicineListService.importNodeInfo(file.getInputStream());
} @GetMapping("/getStatus")
public JSONObject getStatus() {
return medicineListService.getStatus();
}
}
service接口:
public interface MedicineListService extends IService<DrugData> {
JSONObject importNodeInfo(InputStream inputStream) throws Exception;
JSONObject getStatus();
}
service实现类:
@Service
public class MedicineListServiceImpl extends ServiceImpl<MedicineListMapper,DrugData> implements MedicineListService { @Resource
private MedicineListMapper medicineListMapper; private static Logger logger = LoggerFactory.getLogger(MedicineListService.class);
@Autowired
private StringRedisTemplate redisTemplate; public JSONObject importNodeInfo(InputStream in) throws Exception {
redisTemplate.opsForValue().set("isFinished","0",60*60l,TimeUnit.SECONDS);
JSONObject json = new JSONObject();
json.put("msg", "后台正在导入,请稍等!");
json.put("status", true);
new Thread() {
@Override
public void run() {
try {
// 根据类型进行分流导入
String str0 = "";
String str = "";
String fstr = "";
int operCount = 0;
XSSFWorkbook workbook = new XSSFWorkbook(in);
XSSFSheet sheet = workbook.getSheetAt(0);
int totalColumnNum = sheet.getRow(0).getLastCellNum();
logger.info("导入代码信息excel文件的总列数:" + totalColumnNum);
System.out.println(totalColumnNum);
int lastRowNum = sheet.getLastRowNum(); logger.info("导入节点信息excel文件的总行数:" + lastRowNum);
System.out.println(sheet.getLastRowNum());
for (int num = 0; num <= lastRowNum; num++) {
XSSFRow row = sheet.getRow(num);
if(row == null) {
str0 = "存在空数据行,行号:" + (num + 1) + ",导入失败!";
break;
}
int hcount = num + 1;
if (num == 0) {
if (null != String.valueOf(row.getCell(0)) && String.valueOf(row.getCell(0)).equals("药品编码")) {
continue;
} else { json.put("msg", "导入的模板名称出错,请确认");
json.put("status", false);
json.put("data", operCount); }
}
DrugData drugData = new DrugData();
String drugNo = String.valueOf(row.getCell(0));
if(StringUtils.isNotBlank(drugNo) && !"null".equalsIgnoreCase(drugNo)) {
drugData.setDrugno(drugNo);// 药品编码
}
String drugName = String.valueOf(row.getCell(1));
if(StringUtils.isNotBlank(drugName) && !"null".equalsIgnoreCase(drugName)) {
drugData.setDrugname(drugName);//药品名称
}
String indiction = String.valueOf(row.getCell(2));
if(StringUtils.isNotBlank(indiction) && !"null".equalsIgnoreCase(indiction)) {
drugData.setIndiction(indiction); //适应症
} try {
QueryWrapper<DrugData> wrapper = new QueryWrapper<>();
if(StringUtils.isNotBlank(drugData.getDrugno())){
wrapper.eq("drugno",drugData.getDrugno());
}
List<DrugData> drugDataList = medicineListMapper.selectList(wrapper); if (null != drugDataList && drugDataList.size() > 0) {
drugData.setId(drugDataList.get(0).getId());
medicineListMapper.updateById(drugData);
} else {
medicineListMapper.insert(drugData);
}
} catch (Exception e) {
logger.error(e.getMessage());
str = str + "第【" + hcount + "】行,";
continue;
}
operCount++;
}
if (StringUtils.isNotBlank(str)) {
str = "其中-->" + str + "导入失败!";
} if (StringUtils.isNotBlank(fstr)) {
fstr = "==投量-->" + fstr + "附表导入失败!";
}
redisTemplate.opsForValue().set("isFinished","1"); json.put("msg", "操作成功" + str0 + str + fstr);
json.put("status", true);
json.put("data", operCount); } catch (Exception e) {
logger.error(e.getMessage());
} finally {
try {
in.close();
} catch (IOException e) {
logger.error(e.getMessage());
}
}
}
}.start();
return json;
}
@Override
public JSONObject getStatus() {
String isFinished = redisTemplate.opsForValue().get("isFinished");
JSONObject result = new JSONObject();
if (StringUtils.isNotBlank(isFinished)) {
result.put("msg", "获取成功");
result.put("status", true);
result.put("data",isFinished);
} else {
result.put("msg", "获取失败");
result.put("status", false);
}
redisTemplate.delete("isFinished");
return result;
}
}
注意:导入Excel时,String.valueOf(row.getCell(0))获取的值出了要进行非空判断外,还要判断不为字符串null,再执行插入,否则Excel中未填写的单元格插入数据库会变成字符串null。
解决java POI导入Excel超时问题的更多相关文章
- 在java poi导入Excel通用工具类示例详解
转: 在java poi导入Excel通用工具类示例详解 更新时间:2017年09月10日 14:21:36 作者:daochuwenziyao 我要评论 这篇文章主要给大家介绍了关于在j ...
- java poi 导入excel
最近项目需要导入excel,网上有很多例子,自己整合记录下,兼容2003和2007,暂时没有添加图片处理功能. 所需jar包 http://pan.baidu.com/s/1sjPuWDR pack ...
- Java POI导入Excel文件
今天在公司需要做个导入Excel文件的功能,所以研究了一下,参考网上的一些资料总算是做出来了,在此记录一下防止以后忘记怎么弄. 本人用的是poi3.8,所以需要的JAR包如下: poi-3.8.jar ...
- java poi导入Excel(个人代码)
案例使用的框架:jsp+spring+mybaties <form id="importForm" name="importForm" method=&q ...
- java poi导入EXCEL xls文件代码
/** * */ package com.bn.car.common.report.excel; import java.io.FileInputStream; import java.io.IOEx ...
- 解决Java POI 导出Excel时文件名中文乱码,兼容浏览器
String agent = request.getHeader("USER-AGENT").toLowerCase(); response.setContentType(&quo ...
- Java 使用poi导入excel,结合xml文件进行数据验证的例子(增加了jar包)
ava 使用poi导入excel,结合xml文件进行数据验证的例子(增加了jar包) 假设现在要做一个通用的导入方法: 要求: 1.xml的只定义数据库表中的column字段,字段类型,是否非空等条件 ...
- java如何导入Excel文件
Java使用POI导入Excel文件,操作起来比较简单,支持xlsx格式. 下载POI资源包 从官网https://poi.apache.org/下载POI,笔者选择的是版本是3.17,下载后文件名是 ...
- poi导入Excel,数字科学记数法转换
在这里分享一下使用poi 导入Excel时 把数字转换为科学记数法的解决方法: 就是使用DecimalFormat对 i 进行了格式化 结果为:
随机推荐
- UML——构件图
宏观导图: 细节探究: 一.What 构件:是一个应用很广的名词,在建筑工程.机械工程.软件工程中等都有该概念.其实,说道底表达的都是一个意思.就像是标准化生产出来的零部件一样,具有可替换性.同质性, ...
- JVM调优之垃圾定位、垃圾回收算法、垃圾处理器对比
谈垃圾回收器之前,要先讲讲垃圾回收算法,以及JVM对垃圾的认定策略,JVM垃圾回收器是垃圾回收算法的具体实现,了解了前面的前置知识,有利于对垃圾回收器的理解. 什么是垃圾? 垃圾,主要是指堆上的对象, ...
- Pytest(17)运行未提交的git(pytest-picked)
前言 我们每天写完自动化用例后都会提交到 git 仓库,随着用例的增多,为了保证仓库代码的干净,当有用例新增的时候,我们希望只运行新增的未提交 git 仓库的用例.pytest-picked 插件可以 ...
- java中== equal hashcode 比较的区别
== 基本数据类型是进行数值的比较 引用数据类型比较的是两对象的地址值 实际上都是进行直接值的比较 equal(Object) Object型参数,任何类型的实参都可以传入,只有实参是一个字符串且内容 ...
- H - 看病要排队
看病要排队这个是地球人都知道的常识.不过经过细心的0068的观察,他发现了医院里排队还是有讲究的.0068所去的医院有三个医生(汗,这么少)同时看病.而看病的人病情有轻重,所以不能根据简单的先来先服务 ...
- POJ 3281 Dining(最大流板子)
牛是很挑食的.每头牛都偏爱特定的食物和饮料,其他的就不吃了. 农夫约翰为他的牛做了美味的饭菜,但他忘了根据它们的喜好检查菜单.虽然他不可能喂饱所有的人,但他想让尽可能多的奶牛吃上一顿有食物和水的大餐. ...
- 洛谷 P4017 最大食物链计数 (拓扑排序,思维)
题意:有\(n\)个点,连\(m\)条边,求最多有多少条食物链(从头走到为有多少条路径). 题解:之前抽了点时间把拓扑排序补完了,这题其实就是一道拓扑排序的裸题.关于拓扑排序: 1.首先,我们用\ ...
- CF1462-D. Add to Neighbour and Remove
codeforces1462D 题意: 给出一个由n个数组成的数组,现在你可以对这个数组进行如下操作:将数组中的一个元素加到这个元素的两边中的一边,然后将这个元素删掉.若该元素在最左边,那么该元素不能 ...
- XV6学习(15)Lab mmap: Mmap
代码在Github上. 这一个实验是要实现最基础的mmap功能.mmap即内存映射文件,将一个文件直接映射到内存当中,之后对文件的读写就可以直接通过对内存进行读写来进行,而对文件的同步则由操作系统来负 ...
- [视频] Docker 安装 nginx + rtmp
目录 拉取镜像 创建并运行容器,映射出两个端口1935.80 将视频文件推流至rtmp服务器 使用ffplay播放rtmp流 拉取镜像 docker pull alfg/nginx-rtmp 创建并运 ...