前言: 最近做项目遇到了一个需求,上传Excel获取数据更新Excel文档,并直接返回更新完的Excel到前端下载;其实需求并没有什么问题,关键是前端用到的是layui上传组件(layui.upload)踩了不少坑啊;为此写下了如下笔记:

(一)后端:

  public async Task<string> UploadExcelUpdateExcel()
{
var file = Request.Form.Files.FirstOrDefault();//这里只获取一个文件
if (file == null)
throw new UserFriendlyException(L("File_Empty_Error")); long fileSize = file.Length;
if (fileSize > )
throw new UserFriendlyException(L("File_SizeLimit_Error")); string fileExtension = Path.GetExtension(file.FileName).ToLower();
//限定只能上传xls和xlsx
string[] allowExtension = { ".xls", ".xlsx" }; //上传文件类型不正确
if (!allowExtension.Any(x => x == fileExtension))
throw new UserFriendlyException(L("File_Invalid_Type_Error")); //获取本机储存地址
var filePath = Path.Combine(_hostingEnvironment.WebRootPath, "uploadfiles", file.FileName); //写入cookie
var httpContext = IocManager.Instance.Resolve<IHttpContextAccessor>().HttpContext; try
{
var memoryStream = new MemoryStream(); //创建工作台
IWorkbook workbook = null;
//操作Excel
using (var fileStream = file.OpenReadStream())
{
//判断版本2007版本.xlsx,2003版本.xls
if (fileExtension == ".xlsx")
workbook = new XSSFWorkbook(fileStream);
else
workbook = new HSSFWorkbook(fileStream);
//获取第一个sheet
ISheet sheet = workbook.GetSheetAt();
IRow rowSour;//定义行数据
//定义要修改的列索引
var cellIndex = new int[] { , };
//获取第一行的数据
var firstSour = sheet.GetRow();
var input = new ImportExcelSourInput();
input.BeginDate = Convert.ToDateTime(firstSour.GetCell().ToString()).AddMonths(-);
input.EndDate = Convert.ToDateTime(firstSour.GetCell().ToString()).AddMonths(-);
//获取数据源
var usersSour = await _basicSalaryAppService.ImportExcelSourServices(input); //遍历所有行
var cells = sheet.GetRow().PhysicalNumberOfCells;
for (var i = ; i <= sheet.LastRowNum; i++)
{
rowSour = sheet.GetRow(i);
if (rowSour == null || cells != rowSour.PhysicalNumberOfCells)
break;
//获取第三列的这个人的身份证
var IDCard = rowSour.GetCell().ToString();
var thisSour = usersSour.Where(s => s.IDCard == IDCard).FirstOrDefault();
//修改本期收入,住房公积金
if (thisSour != null)
{
rowSour.GetCell(cellIndex[]).SetCellValue(Convert.ToDouble(thisSour.ShouldSalary));
rowSour.GetCell(cellIndex[]).SetCellValue(Convert.ToDouble(thisSour.PublicAccumulationFundsDeduction));
}
else
{
var Name = rowSour.GetCell().ToString();
throw new UserFriendlyException($"系统中:[ {Name} ] 身份证号:( {IDCard} )与Excel数据不一致,请修正员工档案数据!");
}
}
//写入流
workbook.Write(memoryStream);
//恢复起始位置
memoryStream.Position = ;
//关闭
fileStream?.Close();
workbook?.Close();
}
//保存至本地
using (var fileStm = new FileStream(filePath, FileMode.Create))
{
memoryStream.WriteTo(fileStm); fileStm.Dispose();
}
}
catch (Exception e)
{
httpContext.Response.Cookies.Append("xinzi_message", Convert.ToBase64String(Encoding.UTF8.GetBytes($"上传错误!错误消息:{e.Message}")), new CookieOptions
{
Expires = DateTime.Now.AddMinutes()
}); throw new UserFriendlyException($"上传错误!错误消息:{e.Message}");
} var path = $"{httpContext.Request.Scheme}://{httpContext.Request.Host.Value}/uploadfiles/{file.FileName}";
return path;//文件路径;
}

后端上传服务代码

(二)前端:

  //读取cookies
function getCookie(name) {
var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
if (arr = document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
} upload.render({
elem: '#upload-salary'
, url: 'UploadExcelUpdateExcel'
, acceptMime: ".xlsx,.xls"
, before: function (obj) {
abp.ui.setBusy("body") //上传loading
}
, type: "file"
, accept: 'file' //普通文件
, done: function (res) {
var a = document.createElement('a');
a.download = "导出文件名";
a.href = res.result;
$("body").append(a);
a.click();
abp.ui.clearBusy("body") //关闭loading
}
, error: function () {
var base = new Base64();
//获取后端写入的cookie message
var msg = getCookie("xinzi_message");
if (msg != null)
abp.message.error(base.decode(msg));
abp.ui.clearBusy("body") //关闭loading
}
});

前端渲染代码

  存在的问题:

    1),layui.upload上传组件成功后,返回类型应该是不支持返回流文件下载(欢迎大佬们指正解决方案),无奈之举我只有在后端生成好填充完数据的Excel表格,返回服务器文件路径下载了;

    2),layui.upload上传组件失败的时候,服务器端抛出异常消息,前端无法展示,失败回调函数只有两个参数(当前文件的索引,上传函数,官网解释如下图),没有返回参数!(我去什么情况,淡淡的忧桑啊!)

   百般思考终于有了如下解决方案:

      a.后端catch捕获到异常消息后,将异常消息填充到响应的Cookie中;前端异常回调函数里获取Cookie来获取后端传来的消息异常。前后端代码示例:

      b.此时layui默认的上传失败回调函数,会抛出一个消息框上传失败,这里就需要手动改下upload.js的失败回调消息提示了,我是把默认的消息提示删了;

  注:后端返回中文消息前端可能会有显示问题,我个人是后端base64加密,前端解密来完成显示的;

 /**
*
* Base64 encode / decode
*
* @author haitao.tu
* @date 2010-04-26
* @email tuhaitao@foxmail.com
*
*/ function Base64() { // private property
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; // public method for encoding
this.encode = function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = _utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
}
return output;
} // public method for decoding
this.decode = function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = _keyStr.indexOf(input.charAt(i++));
enc2 = _keyStr.indexOf(input.charAt(i++));
enc3 = _keyStr.indexOf(input.charAt(i++));
enc4 = _keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = _utf8_decode(output);
return output;
} // private method for UTF-8 encoding
_utf8_encode = function (string) {
string = string.replace(/\r\n/g, "\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
} }
return utftext;
} // private method for UTF-8 decoding
_utf8_decode = function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while (i < utftext.length) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if ((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i + 1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i + 1);
c3 = utftext.charCodeAt(i + 2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
}

前端base64解密,js代码

layui上传Excel更新数据并下载的更多相关文章

  1. Layui上传文件以及数据表格

    layui对于一些前端小白来说,例如我,真的非常的好用,不用去花很多很多的心思在前端美化中,并且提高了很大的工作效率.所以建议一些觉得自己前端技术不是很强,但是想让前端美化一点的可以使用layui. ...

  2. 使用phpExcel批量上传excel表数据到mysql数据库中

    /*批量上传数据*/ if(isset($_POST['submit']) && $_POST['submit']=='上传文件') { //导入类文件 require_once (& ...

  3. 上传excel数据到数据库中

    上传excel表格数据到数据库 导入固定路径下的excel数据到数据库 <form id="disposeFlightDataForm" action="../up ...

  4. postman上传excel,java后台读取excel生成到指定位置进行备份,并且把excel中的数据添加到数据库

    最近要做个前端网页上传excel,数据直接添加到数据库的功能..在此写个读取excel的demo. 首先新建springboot的web项目 导包,读取excel可以用poi也可以用jxl,这里本文用 ...

  5. ASP.NET Core 2.2 : 十六.扒一扒新的Endpoint路由方案 try.dot.net 的正确使用姿势 .Net NPOI 根据excel模板导出excel、直接生成excel .Net NPOI 上传excel文件、提交后台获取excel里的数据

    ASP.NET Core 2.2 : 十六.扒一扒新的Endpoint路由方案   ASP.NET Core 从2.2版本开始,采用了一个新的名为Endpoint的路由方案,与原来的方案在使用上差别不 ...

  6. SpringBoot(十三)_springboot上传Excel并读取excel中的数据

    今天工作中,发现同事在整理数据,通过excel上传到数据库.所以现在写了篇利用springboot读取excel中的数据的demo.至于数据的进一步处理,大家肯定有不同的应用场景,自行修改 pom文件 ...

  7. jsp+servlet上传excel并将数据导入到数据库表的实现方法

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  8. java的poi技术下载Excel模板上传Excel读取Excel中内容(SSM框架)

    使用到的jar包 JSP: client.jsp <%@ page language="java" contentType="text/html; charset= ...

  9. 【asp.net】asp.net实现上传Excel文件并读取数据

    #前台代码:使用服务端控件实现上传 <form id="form1" runat="server"> <div> <asp:Fil ...

随机推荐

  1. spark streaming 接收kafka消息之三 -- kafka broker 如何处理 fetch 请求

    首先看一下 KafkaServer 这个类的声明: Represents the lifecycle of a single Kafka broker. Handles all functionali ...

  2. 面试还不知道BeanFactory和ApplicationContext的区别?

    接口 BeanFactory 和 ApplicationContext 都是用来从容器中获取 Spring beans 的,但是,他们二者有很大不同 我看到过很多问 BeanFactory 和 App ...

  3. kuberbetes基础概念

    部署了一大堆,来了解一下K8S一些基本的概念. 1.Node Node作为集群中的工作节点,运行真正的应用程序,在Node上Kubernetes管理的最小运行单元是Pod.Node上运行着Kubern ...

  4. SQL 对float类型列进行排序引发的异常

    车祸现场 要求:根据学分和完成时间获取前200名学员,当学分相同时,完成时间较早的排在前面 可以明显看到,完成时间为4.1号的记录排在了3.27号前面. 事故原因 float 表示近似数值,存在精度损 ...

  5. 深入理解Java内存模型JMM与volatile关键字

    深入理解Java内存模型JMM与volatile关键字 多核并发缓存架构 Java内存模型 Java线程内存模型跟CPU缓存模型类似,是基于CPU缓存模型来建立的,Java线程内存模型是标准化的,屏蔽 ...

  6. ZooKeeper入门(三) ZooKeeper数据模型

    1 简述 ZooKeeper可以看成一种高可用性的文件系统,但是,它没有文件和目录,而是使用节点,称为znode. znode可以作为保存数据的容器(如同文件),也可以作为保存其他节点的容器(如同目录 ...

  7. Python爬虫入门:爬取豆瓣电影TOP250

    一个很简单的爬虫. 从这里学习的,解释的挺好的:https://xlzd.me/2015/12/16/python-crawler-03 分享写这个代码用到了的学习的链接: BeautifulSoup ...

  8. 开源:C# 代码自动生成工具,支持站点前后台

    前言 写这个项目有很长一段时间了,期间也修修改改,写到最后,自己也没咋用(研究方向变化了). 正文 具体项目开源了:https://github.com/supperlitt/WebAutoCodeO ...

  9. c++学习书籍推荐《Visual C++2008入门经典》下载

    百度云及其他网盘下载地址:点我 <Visual C++2008入门经典>学习目标: 使用标准模板库(STL)来组织和操作本地C++程序中的数据 C++程序调试技术 构造Microsoft ...

  10. .net core2学习笔记

    在Linux上安装完netcore的sdk后,发现每次重新登录dotnet命令都会失效,咨询完同事后才知道之前的设置只是临时变量,需要vim /etc/profile   编辑这个文件,把环境变量写入 ...