.NET Core使用NPOI导出多语言Excel-Base64加密-解密下载-带源码

实现了前端通过调取导出接口,返回Base64加密地址,再去解密,暂停下载的效果

头部 中文 繁体 日语 英语

效果图:

接下来是实现代码

        /// <summary>
        /// 导出数据
        /// </summary>
        /// <param name="bs">id</param>
        /// <returns></returns>
        [HttpPost]
        [Route("ExportList")]
        public async Task<ApiResult> ExportList(BaseQuery<BasicSettingsQuery> query)
        {
            try
            {
                //判斷當前頁
                if (query.oData.pageIndex <= 0)
                {
                    query.oData.pageIndex = 1;
                }

                if (query.oData.pageSize <= 0)
                {
                    query.oData.pageSize = GlobalConstants.MIN_PAGE_NUM;
                }

                if (query.oData.pageSize > GlobalConstants.MAX_PAGE_NUM)
                {
                    query.oData.pageSize = GlobalConstants.MAX_PAGE_NUM;
                }
                query.oData.company_id = GetCompany();

                //查出DataTable格式的数据
                var data = await _overtimeService.QueryExportData(query.oData);
                DataTable table = data;

                //需要加根据语言返回对应的字段(语言名称)
                Dictionary<string, string> dic = new Dictionary<string, string>();
                dic.Add("ST_PREFIX_NO", MultilingualCodeEnum.ST_PREFIX_NO.GetMessage());
                dic.Add("DT_ACTION_DATE", MultilingualCodeEnum.DT_ACTION_DATE.GetMessage());
                dic.Add("ST_STAFF_NAME", MultilingualCodeEnum.ST_STAFF_NAME.GetMessage());
                dic.Add("ST_DEPARTMENT_ID", MultilingualCodeEnum.ST_DEPARTMENT_ID.GetMessage());
                dic.Add("ST_STOCK_ID", MultilingualCodeEnum.ST_STOCK_ID.GetMessage());
                dic.Add("NO_TOT_HOUR", MultilingualCodeEnum.NO_TOT_HOUR.GetMessage());
                dic.Add("NO_MARK", MultilingualCodeEnum.NO_MARK.GetMessage());

                if (table == null)
                {
                    // 導出出錯,請聯繫管理員
                    return ApiResult.sysError(ApplicationErrorCodeEnum.FAILURE.GetMessage());

                }
                if (table.Rows.Count == 0)
                {
                    //  暫無數據可導出
                    return ApiResult.sysError(ApplicationErrorCodeEnum.FAILURE.GetMessage());
                }


                NPOIHelper npoi = new NPOIHelper();

                //导出文件存放地址
                string path = npoi.SaveFromDataTable(RemoveEnterCharFromTable(table), "OT", "/impexp/export/Overtime/", "sheet", null, dic);
                if (path == null)
                {
                    //为空
                    return ApiResult.sysError(ApplicationErrorCodeEnum.FAILURE.GetMessage());

                }
                //加密
                var a = EncryptionHelper.DouBase64Encode(path);
                if (a != null)
                {
                    // 获取 "Constants" 下的 "ApiUrl" 值
                    string apiUrl = _options.CurrentValue.ApiUrl;

                    //返回域名+下载文件的控制器/方法名传?加密数据
                    string url = apiUrl + "api/download/ddown?id=" + a;
                    //成功
                    return ApiResult.success(new { url = url });
                }
                else
                {
                    //失败
                    return ApiResult.sysError(ApplicationErrorCodeEnum.FAILURE.GetMessage());

                }

            }
            catch (Exception ex)
            {
                //導出出錯,請聯繫管理員
                _loggerContext.Error(ex.Message, "加班申请 導出出錯");

                return ApiResult.sysError(ApplicationErrorCodeEnum.FAILURE.GetMessage());

            }
        }


        /// <summary> 
        /// 刪除table的換行符 *** 
        /// </summary>
        /// <param name="table"></param>
        /// <returns></returns>
        private DataTable RemoveEnterCharFromTable(DataTable table)
        {
            if (table == null)
            {
                return null;
            }

            int rowLen = table.Rows.Count;
            int colLen = table.Columns.Count;

            for (int i = 0; i < rowLen; i++)
            {
                DataRow row = table.Rows[i];
                for (int j = 0; j < colLen; j++)
                {
                    string val = row[j] == null ? string.Empty : row[j].ToString();
                    if (!string.IsNullOrWhiteSpace(val))
                    {
                        string newVal = val.Replace("\r", " ").Replace("\n", " ").Trim();
                        if (val != newVal)
                        {
                            row[j] = newVal;
                        }
                    }
                }
            }
            return table;
        }

SaveFromDataTable()方法 

   /// <summary>
   ///  從DataTable中讀取數據放入Excel中,并保存到服務器指定文件夾中去掉了info
   /// </summary>
   /// <param name="table">導入的table集</param>
   /// <param name="fileName">文件名</param>
   /// <param name="saveDir">保存目錄</param>
   /// <param name="info">返回錯誤信息</param> 
   /// <param name="sheetName">sheet名稱</param>
   /// <param name="hideCell">隱藏列</param>
   /// <param name="dic">
   /// 轉化表頭(中文轉英文)
   /// (string, string) (待轉化的字段,轉化為的字段)
   /// </param>
   /// <returns>返回路徑</returns>
   public string SaveFromDataTable(DataTable dataSet, string fileName, string saveDir, string sheetName = null, int[] hideCell = null, Dictionary<string, string> dic = null)
   {
       try
       {
           //初始化工作簿實例
           HSSFWorkbook workbook = new HSSFWorkbook();

           NPOICommonHelper.SetWorkbook(workbook, dataSet, sheetName, hideCell, dic);

       

           string result = NPOISaveHelper.Save(workbook, fileName, saveDir);

           return result;
       }
       catch (ThreadAbortException)
       {
           //終止線程,不做處理
           return null;
       }
       catch (Exception ex)
       {
           return null;
       }
   }

 Save()方法

   /// <summary>
   /// 保存服務端的默認路徑
   /// </summary>
   private static string directory = "/impexp/export/";

   /// <summary>
   /// 保存到服務器指定目錄
   /// </summary>
   /// <param name="workbook">數據</param>
   /// <param name="fileName">文件名</param>
   /// <param name="saveDir">保存目錄</param>
   /// <returns>返回目錄路徑。如果為null/空,則保存失敗,需看日誌文件瞭解情況</returns>
   public static string Save(HSSFWorkbook workbook, string fileName, string saveDir)
   {
       string result = string.Empty;
       //導出
       using (MemoryStream ms = new MemoryStream())
       {
           workbook.Write(ms);
           result = SaveByWeb(ms, DateTime.Now.ToString("yyyyMMddHHmmssFFF")+"-"+ fileName + "-" + DateTime.Now.ToString("yyyyMMddHHmmssFFF") + ".xls", saveDir);
           workbook = null;
       }
       return result;
   }

api/download/ddown 是一个接口 接下来是download控制器的代码

using System;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using JWProject.Utils.NPOI.common;

namespace JWProject.Api.Controllers
{
    /// <summary>
    /// 下载
    /// </summary>
    [CtmApiExplorerSettings(GroupName = "特别功能接口")]
    [Route("api/[controller]")]
    [ApiController]
    public class DownloadController : Controller
    {
        private readonly IWebHostEnvironment _webHostEnvironment;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="webHostEnvironment"></param>
        public DownloadController(IWebHostEnvironment webHostEnvironment)
        {
         
            _webHostEnvironment = webHostEnvironment;
        }

        /// <summary>
        /// 下载数据
        /// </summary>
        /// <param name="bs">id</param>
        /// <returns></returns>
        [HttpGet]
        [Route("ddown")]
        public async Task<ActionResult> ddown(string id)
        {
            try
            {
                if (!string.IsNullOrEmpty(id))
                {
                    //将地址解密
                    string value = EncryptionHelper.DouBase64Decode(id);
                    return await downloadfile(value);
                }
                else
                {
                    Response.StatusCode = 404;
                }
            }
            catch
            {
                Response.StatusCode = 404;
            }
            return Content("ok");
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        private async Task< ActionResult> downloadfile(string path)
        {
            try
            {
                if (!string.IsNullOrEmpty(path))
                {
                    string[] fileInfo = path.Split('.');
                    //判断文件格式
                    if (!GetSuffix(fileInfo[fileInfo.Length - 1]))
                    {
                        Response.StatusCode = 404;
                        // return;
                    }
                    else
                    {
                        string allPath = _webHostEnvironment.ContentRootPath + path;
                        if (FileHelper.CheckFile(allPath))
                        {
  //下载帮助文件
                            bool succ = await DownloadHelper.DownloadFileAsync(Request, allPath, 1024 * 1024 * 12);//10M/s:12000000
                            if (!succ)
                            {
                                Response.StatusCode = 404;
                            }
                        }
                        else
                        {
                            Response.StatusCode = 404;
                        }
                    }
                }
                else
                {
                    Response.StatusCode = 404;
                }
            }
            catch
            {
                Response.StatusCode = 404;
            }
            return Content("ok");
        }

        /// <summary>
        /// 文件后缀名类型
        /// </summary>
        /// <param name="suffix"></param>
        /// <returns></returns>
        private bool GetSuffix(string suffix)
        {
            string value = suffix.ToLower();
            bool result = true;
            switch (value)
            {
                case "asax":
                case "ascx":  //Global.asax 
                case "ashx":
                case "asmx":  //WebService.asmx  
                case "aspx":  //index.aspx

                case "browser":

                case "cd":
                case "config":
                case "cshtml":
                case "cs":  //index.aspx.cs 
                case "css":  //

                case "dbml":
                case "dll":  //LinkOraDB.dll

                case "htm":  //FileNotFound.htm
                case "html":

                case "js":  //kindeditor.js

                case "master":  //mainpage.master

                case "xml":  //MenuCHI.xml
                case "xsd":  //dsLabel.xsd
                case "xslt":

                case "rdlc": //
                case "resx": //lang.en-us.resx
                case "rpt":  //printlbl.rpt

                case "skin":
                case "sitemap":
                case "swf":  //uploadify.swf
                case "svc":

                    //case "ttf":  //code128.ttf
                    result = false;
                    break;
                default:
                    result = true;
                    break;
            }
            return result;
        }

    }
}

 写下来是 //下载帮助文件  DownloadHelper.cs

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace JWProject.Common.Helpers
{
    /// <summary>
    ///downloadHelper 的摘要说明
    /// </summary>
    public class DownloadHelper
    {
        public DownloadHelper()
        {
            //
            //TODO: 在此处添加构造函数逻辑
            //
        }

        /// <summary>
        /// 下載數據到指定文件中
        /// </summary>
        /// <param name="context">当前请求的HttpContext</param>
        /// <param name="filePath">绝对路徑</param>
        /// <param name="fileName">客户端保存的文件名</param>
        public static async Task DownloadFileAsync(HttpRequest context, string filePath, string fileName)
        {
            try
            {
                HttpContext httpContext = context.HttpContext;
                FileInfo fileInfo = new FileInfo(filePath);
                FileStream myFile = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                byte[] buffur = new byte[myFile.Length];
                myFile.Read(buffur, 0, (int)myFile.Length);

                httpContext.Response.Clear();
                httpContext.Response.Headers.Clear();
                httpContext.Response.Headers["Content-Disposition"] = "attachment;filename=" + fileName;
                httpContext.Response.Headers["Content-Length"] = fileInfo.Length.ToString();
                httpContext.Response.Headers["Content-Transfer-Encoding"] = "binary";
                httpContext.Response.ContentType = "application/octet-stream";
                //HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.GetEncoding("gb2312");
                await httpContext.Response.Body.WriteAsync(buffur, 0, (int)myFile.Length);
                httpContext.Response.Body.Flush();
                //HttpContext.Current.Response.End();
            }
            catch (Exception ex) { }
        }


        #region 下載服務
        /**/
        /// <summary>
        /// 下载文件,支持大文件、续传、速度限制。支持续传的响应头Accept-Ranges、ETag,请求头Range 。
        /// Accept-Ranges:响应头,向客户端指明,此进程支持可恢复下载.实现后台智能传输服务(BITS),值为:bytes;
        /// ETag:响应头,用于对客户端的初始(200)响应,以及来自客户端的恢复请求,
        /// 必须为每个文件提供一个唯一的ETag值(可由文件名和文件最后被修改的日期组成),这使客户端软件能够验证它们已经下载的字节块是否仍然是最新的。
        /// Range:续传的起始位置,即已经下载到客户端的字节数,值如:bytes=1474560- 。
        /// 另外:UrlEncode编码后会把文件名中的空格转换中+(+转换为%2b),但是浏览器是不能理解加号为空格的,所以在浏览器下载得到的文件,空格就变成了加号;
        /// 解决办法:UrlEncode 之后, 将 "+" 替换成 "%20",因为浏览器将%20转换为空格
        /// </summary>
        /// <param name="context">当前请求的HttpContext</param>
        /// <param name="filePath">下载文件的物理路径,含路径、文件名</param>
        /// <param name="speed">下载速度:每秒允许下载的字节数</param>
        /// <returns>true下载成功,false下载失败</returns>
        public static async Task<bool> DownloadFileAsync(HttpRequest context, string filePath, long speed)
        {
            HttpContext httpContext = context.HttpContext;
            httpContext.Response.Clear();
            bool ret = true;
            try
            {
                #region --验证:HttpMethod,请求的文件是否存在#region
                switch (httpContext.Request.Method.ToUpper())
                { //目前只支持GET和HEAD方法
                    case "GET":
                    case "HEAD":
                        break;
                    default:
                        httpContext.Response.StatusCode = 501;
                        return false;
                }
                if (!File.Exists(filePath))
                {
                    httpContext.Response.StatusCode = 404;
                    return false;
                }
                #endregion

                #region 定义局部变量#region 定义局部变量
                long startBytes = 0;
                long stopBytes = 0;
                int packSize = 1024 * 10; //分块读取,每块10K bytes
                string fileName = Path.GetFileName(filePath);
                FileStream myFile = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                BinaryReader br = new BinaryReader(myFile);
                long fileLength = myFile.Length;

                int sleep = (int)Math.Ceiling(1000.0 * packSize / speed);//毫秒数:读取下一数据块的时间间隔
                string lastUpdateTiemStr = File.GetLastWriteTimeUtc(filePath).ToString("r");
                string eTag = HttpUtility.UrlEncode(fileName, Encoding.UTF8) + lastUpdateTiemStr;//便于恢复下载时提取请求头;
                #endregion

                #region --验证:文件是否太大,是否是续传,且在上次被请求的日期之后是否被修改过
                if (myFile.Length > long.MaxValue)
                {//-------文件太大了-------
                    httpContext.Response.StatusCode = 413;//请求实体太大
                    return false;
                }

                if (httpContext.Request.Headers.ContainsKey("If-Range"))//对应响应头ETag:文件名+文件最后修改时间
                {
                    //----------上次被请求的日期之后被修改过--------------
                    StringValues ifrange = string.Empty;
                    httpContext.Request.Headers.TryGetValue("If-Range", out ifrange);
                    if (!string.IsNullOrWhiteSpace(ifrange) && ifrange.ToString().Replace("\"", "") != eTag)
                    {//文件修改过
                        httpContext.Response.StatusCode = 412;//预处理失败
                        return false;
                    }
                }
                #endregion

                try
                {
                    #region -------添加重要响应头、解析请求头、相关验证
                    httpContext.Response.Clear();

                    if (httpContext.Request.Headers.ContainsKey("Range"))
                    {//------如果是续传请求,则获取续传的起始位置,即已经下载到客户端的字节数------
                        httpContext.Response.StatusCode = 206;//重要:续传必须,表示局部范围响应。初始下载时默认为200
                        StringValues rangeStr = string.Empty;
                        httpContext.Request.Headers.TryGetValue("Range", out rangeStr);
                        if (string.IsNullOrWhiteSpace(rangeStr))
                        {
                            return false;
                        }
                        string[] ranges = rangeStr.ToString().Split(new char[] { '=', '-' });//"bytes=1474560-"
                        startBytes = Convert.ToInt64(ranges[1]);//已经下载的字节数,即本次下载的开始位置  
                        if (startBytes < 0 || startBytes >= fileLength)
                        {//无效的起始位置
                            return false;
                        }
                    }

                    //httpContext.Response.Buffer = false;
                    httpContext.Response.Headers["Content-MD5"] = EncryptionHelper.Md532(filePath);//用于验证文件
                    httpContext.Response.Headers["Accept-Ranges"] = "bytes";//重要:续传必须
                    httpContext.Response.Headers["ETag"] = "\"" + eTag + "\"";//重要:续传必须
                    httpContext.Response.Headers["Last-Modified"] = lastUpdateTiemStr;//把最后修改日期写入响应                
                    httpContext.Response.ContentType = "application/octet-stream";//MIME类型:匹配任意文件类型
                    httpContext.Response.Headers["Content-Disposition"] = "attachment;filename=" + HttpUtility.UrlEncode(fileName, Encoding.UTF8).Replace("+", "%20");
                    httpContext.Response.ContentLength = fileLength - startBytes;
                    httpContext.Response.Headers["Connection"] = "Keep-Alive";
                    //httpContext.Response.ContentEncoding = Encoding.UTF8;
                    if (startBytes > 0)
                    {//------如果是续传请求,告诉客户端本次的开始字节数,总长度,以便客户端将续传数据追加到startBytes位置后----------
                        httpContext.Response.Headers["Content-Range"] = string.Format("bytes {0}-{1}/{2}", startBytes, fileLength - 1, fileLength);
                    }
                    #endregion

                    #region -------向客户端发送数据块-------------------
                    br.BaseStream.Seek(startBytes, SeekOrigin.Begin);
                    int maxCount = (int)Math.Ceiling((fileLength - startBytes + 0.0) / packSize);//分块下载,剩余部分可分成的块数
                    for (int i = 0; i < maxCount && !httpContext.RequestAborted.IsCancellationRequested; i++)
                    {
                        //客户端中断连接,则暂停
                        if (maxCount - 1 == i)
                        {
                            packSize = (int)(fileLength - startBytes - i * packSize);
                        }
                        await httpContext.Response.Body.WriteAsync(br.ReadBytes(packSize), 0, packSize);
                        await httpContext.Response.Body.FlushAsync();
                        //httpContext.Response.Body.Flush();
                        if (sleep > 1) Thread.Sleep(sleep);
                    }
                    #endregion
                }
                catch (Exception ex)
                {
                    ret = false;
                }
                finally
                {
                    br.Close();
                    myFile.Close();
                }
            }
            catch (Exception ex)
            {
                ret = false;
            }
            return ret;
        }

        #endregion
    }
}

接下来设置多语言

上传到了资源 自行下载 (LangCodeEnumCodeEnumeration)语言文件的引用文件

引用的LangCodeEnum在下方创建对应文件.cs


    /// <summary>
    /// 多语言码设置
    /// </summary>
    public abstract class LangCodeEnum : CodeEnumeration
    {
        public abstract string GetMessage();
    }

接下来就是设置多语言的MultilingualCodeEnum.cs代码

using GLProject.Common.Extensions;
using JWProject.Common.App_GlobalResources;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JWProject.Common.Enums.Exceptions
{
    /// <summary>
    /// 多语言
    /// </summary>
    public class MultilingualCodeEnum : LangCodeEnum
    {
        public static MultilingualCodeEnum ST_STAFF_NAME = new MultilingualCodeEnum("0", "职员", "Staff", "職員");
        public static MultilingualCodeEnum ST_PREFIX_NO = new MultilingualCodeEnum("1", "编号", "Code", "コード");
        public static MultilingualCodeEnum DT_ACTION_DATE = new MultilingualCodeEnum("2", "日期", "Date", "日期");
        public static MultilingualCodeEnum ST_DEPARTMENT_ID = new MultilingualCodeEnum("3", "部门", "Department", "部門");
        public static MultilingualCodeEnum ST_STOCK_ID = new MultilingualCodeEnum("4", "库存点", "Stock", "庫存點");
        public static MultilingualCodeEnum NO_TOT_HOUR = new MultilingualCodeEnum("5", "总时数", "Hours", "總時數");
        public static MultilingualCodeEnum NO_MARK = new MultilingualCodeEnum("6", "状态", "Status", "ステータス");




        public MultilingualCodeEnum(string code, string Cn, string En, string Ja)
        {
            this.Code = code;
            this.Cn = Cn;
            this.En = En;
            this.Ja = Ja;
        }

        /**
        * 编号
        */
        private string Code;

        /**
         * 中文
         */
        private string Cn;

        /**
         * 英文
         */
        private string En;

        /**
        * 日语
        */
        private string Ja;


        public override string GetMessage()
        {
            LangType langType = LangType.FromLanguageCodes<LangType>(lang.LangType);
            if (langType == LangType.English)
            {
                return En;
            }
            else if (langType == LangType.Japanese)
            {
                return Ja;
            }
            else if (langType == LangType.Chinese)
            {
                return Cn.ToSimplified();
            }
            else
            {
                return Cn.ToTraditional();
            }
        }

    }



}

测试 导出文件返回给前端加密url: 

测试 返回前端解密url下载文件:

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猫猫.net

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值