导出Excel 批量大数据异步多线程

最近接了一个新需求,主要有三个功能:

1.导出1688店铺的订单
2.导出入住微供市场店铺的商品
3.给微供市场进行下订单



其中如何调用阿里的接口进行数据获取这里就不做介绍了,但是如果有同学需要这方面的帮助可以联系我。
这里主要讲述如何导出excel。
最先开始的思路就是通过接口获取到订单信息之后通过页面Response进行输出一个Excel.
新建一个aspx页面。然后写一个方法如下:

        /// <summary>
        /// 导出Excel
        /// </summary>
        /// <param name="lst"></param>
        /// <param name="loginId"></param>
        private void ExportExcel(List<OrdersItemDetailInfo> lst, string loginId)
        {

            string name = string.Format("{0}{1}订单报表.xls", loginId, DateTime.Now.ToString("yyMMddHHmmss"));
            HttpContext.Current.Response.Clear();
            string styleText = @"<style type='text/css'> .text{ mso-number-format:'\@'; } </style> ";
            HttpContext.Current.Response.Charset = "UTF-8";
            HttpContext.Current.Response.ContentEncoding = Encoding.UTF8;//注意编码
            HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(name, Encoding.UTF8).ToString());
            HttpContext.Current.Response.ContentType = "application/ms-excel";
            var sbHtml = new StringBuilder();
            sbHtml.Append("<table border='1' cellspacing='0' cellpadding='0'>");
            sbHtml.Append("<tr>");
            var lstTitle = new List<string> { "订单编号", "订单状态", "买家id", "买家联系电话", "买家收货地址", "创建时间","付款时间","商品ID",
                "分类编号","商品名称", "数量", "商品单价", "付款时分摊后的每笔子订单金额", "产品金额", "折扣价",
                "产品打折单价",  "明细单价","卖家公司名称","运单号码","物流编号","物流公司","收货人姓名","收货手机","收货电话","收货省","收货市","收货区","收货地址" };
            foreach (var item in lstTitle)
            {
                sbHtml.AppendFormat("<td style='font-size: 14px;text-align:center;background-color: #DCE0E2; font-weight:bold;' height='25'>{0}</td>", item);
            }
            sbHtml.Append("</tr>");
            if (lst != null && lst.Count > 0)
            {
                foreach (var item in lst)
                {
                    sbHtml.Append("<tr>");
                    sbHtml.AppendFormat("<td class='text' style='font-size: 12px;height:20px;'>{0}</td>", item.id);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.statusDisPlay);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.buyerLoginId);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", string.Empty);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.toArea);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", Utility.ChangeDate(item.gmtCreate));
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", Utility.ChangeDate(item.gmtPayment));
                    sbHtml.AppendFormat("<td  class='text' style='font-size: 12px;height:20px;'>{0}</td>", item.itemId.ToString());
                    sbHtml.AppendFormat("<td  class='text' style='font-size: 12px;height:20px;'>{0}</td>", item.categoryId.ToString());
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.productName);
                    sbHtml.AppendFormat("<td  style='font-size: 12px;height:20px;'>{0}</td>", item.quantity);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.price);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.actualPayFee);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.amount);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.discount);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.discountPrice);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.unitPrice);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.sellerCompanyName);
                    sbHtml.AppendFormat("<td  class='text' style='font-size: 12px;height:20px;'>{0}</td>", item.logisticsBillNo);
                    sbHtml.AppendFormat("<td  class='text' style='font-size: 12px;height:20px;'>{0}</td>", item.logisticsId);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.logisticsCompanyName);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.receiverName);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.receiverMobile);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.receiverPhone);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.receiverProvince);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.receiverCity);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.receiverCounty);
                    sbHtml.AppendFormat("<td style='font-size: 12px;height:20px;'>{0}</td>", item.receiverAddress);
                    sbHtml.Append("</tr>");
                }
            }
            sbHtml.Append("</table>");
            var charset = "<meta http-equiv=\"content-type\" content=\"application/vnd.ms-excel; charset=utf-8\"/>";
            HttpContext.Current.Response.Write(charset + styleText + sbHtml.ToString());
            HttpContext.Current.Response.End();
        }

其中遇到了几个问题:

问题1:当列的值是一个long长整型的时候,打开excel的时候就会显示成科学计数法,如图产品编号就变成科学计数的方式显示了。
问题2:结果导出Excel 真实数据才几十KB.但是Excel文件却有好几百。原因就是输除了大量的html标签。下图是一个200行数据的文件,相差已经是258kb 和41kb的区别了,当2000行的时候差距更明显。
问题3:点击了导出按钮,跳转到导出页面,执行导出相关代码流程,客户端一直等待服务器响应结束,但是因为数据太多处理时间需要几分钟甚至十几分钟,这样就会出现响应超时。




问题1解决办法:导出的额时候给td加一个样式。

string styleText = @".text{ mso-number-format:'\@'; }

问题2解决办法:使用了一个第三方操作excel的类库 NPOI ,http://npoi.codeplex.com/releases 类库地址,有源码。

        /// <summary>
        /// 导出Excel
        /// </summary>
        /// <param name="lst"></param>
        /// <param name="loginId"></param>
        private void ExportExcel(List<ProductExInfo> lst, string loginId)
        {
            string name = string.Format("{0}销售商品列表.xls", loginId);
            var lstTitle = new List<string> { "产品编号", "状态", "标题", "业务类型", "SKU", "specId", "单品货号", "属性", "单价", "建议零售价", "可销售数量", "单位", "发货地址ID", "运费模板ID", "体积", "产品链接" };

            NPOI.HSSF.UserModel.HSSFWorkbook book = new NPOI.HSSF.UserModel.HSSFWorkbook();//创建工作簿
            NPOI.SS.UserModel.ISheet sheet = book.CreateSheet("销售商品列表");//创建sheet
            NPOI.SS.UserModel.IRow row = sheet.CreateRow(0);//创建表头第一行
            for (int i = 0; i < lstTitle.Count; i++)
            {
                row.CreateCell(i).SetCellValue(lstTitle[i]);//循环创建单元格并且赋值
            }
            for (int j = 0; j < lst.Count; j++)
            {
                NPOI.SS.UserModel.IRow rowt = sheet.CreateRow(j + 1);
                var rcol = rowt.CreateCell(0);
                rcol.SetCellType(NPOI.SS.UserModel.CellType.String);
                rcol.SetCellValue(lst[j].productID.ToString());              
                var rco15 = rowt.CreateCell(15);
                HSSFHyperlink link = new HSSFHyperlink(HyperlinkType.Url);//建一个HSSFHyperlink实体,指明链接类型为URL(这里是枚举,可以根据需求自行更改)  
                link.Address = string.Format(@"https://detail.1688.com/offer/{0}.html", lst[j].productID);//给HSSFHyperlink的地址赋值  
                rco15.Hyperlink = link;//将链接方式赋值给单元格的Hyperlink即可将链接附加到单元格上                   
                rco15.SetCellValue("链接");
            }
            using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
            {
                book.Write(ms);
                Response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}.xls", name));
                Response.BinaryWrite(ms.ToArray());
                book = null;
                ms.Close();
                ms.Dispose();
                Response.End();
            }
        }

使用NPOI 进行导出Excel 同时也解决了第一个问题。
问题3解决办法:只能通过异步的方式去做了。


大致思路就是通过接口查询出订单然后生成一个excel文件在本地然后提供下载链接。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;

namespace Ali1688.Web
{
    /// <summary>
    /// 生成Excel类
    /// </summary>
    public static class ExportExcelManager
    {
        //全局静态变量用来保存当前执行的logs
        public static List<string> ExportLogs = new List<string>();

        /// <summary>
        /// 导出单个店铺的订单
        /// </summary>
        /// <param name="dts"></param>
        /// <param name="dte"></param>
        /// <param name="memberId"></param>
        /// <param name="refreshToken"></param>
        /// <param name="accessToken"></param>
        /// <param name="loginid"></param>
        public static void Report(DateTime dts, DateTime dte, string memberId, string refreshToken, string accessToken, string loginId)
        {
            ExportLogs.Clear();
            ExportLogs.Add("开始导出" + loginId + dts.ToString("yyyy-MM-dd") + "到" + dte.ToString("yyyy-MM-dd") + "的订单");
            List<OrdersItemDetailInfo> lst = new List<OrdersItemDetailInfo>();
            lst = GetOrdersItem(memberId, accessToken, dts, dte);
            ExportLogs.Add("订单获取完成开始生成Excel...");
            string name = loginId + dts.ToString("yyyy-MM-dd") + "到" + dte.ToString("yyyy-MM-dd");
            ExportExcel(lst, name, false);
        }

        /// <summary>
        /// 导出Excel
        /// </summary>
        /// <param name="lst"></param>
        /// <param name="loginId"></param>
        private static void ExportExcel(List<OrdersItemDetailInfo> lst, string loginId, bool isBatch = true)
        {

            string name = string.Format("{0}订单报表{1}.xls", loginId, DateTime.Now.ToString("yyyyMMddHHmmss"));
            var lstTitle = new List<string> { "订单编号", "订单状态", "买家id", "买家联系电话", "买家收货地址", "创建时间","付款时间","商品ID",
                 "分类编号","商品名称", "数量", "商品单价", "付款时分摊后的每笔子订单金额", "产品金额", "折扣价",
                 "产品打折单价",  "明细单价","卖家公司名称","运单号码","物流编号","物流公司","收货人姓名","收货手机","收货电话","收货省","收货市","收货区","收货地址" };
            NPOI.HSSF.UserModel.HSSFWorkbook book = new NPOI.HSSF.UserModel.HSSFWorkbook();
            NPOI.SS.UserModel.ISheet sheet = book.CreateSheet("订单列表");
            NPOI.SS.UserModel.IRow row = sheet.CreateRow(0);
            for (int i = 0; i < lstTitle.Count; i++)
            {
                row.CreateCell(i).SetCellValue(lstTitle[i]);
            }
            ExportLogs.Add("共需要生成" + lst.Count + "条明细!");
            for (int j = 0; j < lst.Count; j++)
            {
                NPOI.SS.UserModel.IRow rowt = sheet.CreateRow(j + 1);
                //创建单元格
                ExportLogs.Add("生成完成" + (j + 1) + " 条明细!");
            }
            ExportLogs.Add("所有明细生成完成,生成文件中。。。");
            // 写入到客户端  
            using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
            {
                book.Write(ms);
                string path = System.AppDomain.CurrentDomain.BaseDirectory + (isBatch ? "/BatchReport/" : "/Report/") + name;
                var dir = System.AppDomain.CurrentDomain.BaseDirectory + (isBatch ? "/BatchReport" : "/Report");
                if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
                using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
                {
                    byte[] data = ms.ToArray();
                    fs.Write(data, 0, data.Length);
                    fs.Flush();
                }
                book = null;
            }
            ExportLogs.Add("生成Excel文件完成!请<a href='" + (isBatch ? "/BatchReport/" : "/Report/") + name + "'>点击下载</a>");
        }

        /// <summary>
        /// 获取这个商铺的销售订单
        /// </summary>
        /// <param name="memberId"></param>
        /// <param name="accessToken"></param>
        /// <param name="dts"></param>
        /// <param name="dte"></param>
        /// <param name="pageIndex"></param>
        /// <returns></returns>
        private static List<OrdersItemDetailInfo> GetOrdersItem(string memberId, string accessToken, DateTime dts, DateTime dte)
        {
            //获取订单具体方法
            throw new NotImplementedException();
        }
    }
}

调用代码:

   Thread thread = new Thread(() => ExportExcelManager.Report(dts, dte, memberId, refreshToken, accessToken, loginId));
               thread.Start();

最后来几张图片吧

1.主页面:



2.导出页面
3.导出成功

最后其实还有一个问题我没有解决掉,就是这个提示,其实是一个全局的静态变量。所以多个用户同时操作的时候会出现提示不准确。但是这个需求是我们内部的人员使用而且只是提示上面的不准确。
如果谁有好的提议可提出来我修改。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容