C# Json数据转DataTable并生成PDF在线下载--iTextSharp生成PDF实例(文件下载,json数据转换,PDF排版一步到位)

前言

本文将重点介绍iTextSharp的使用方法和易踩的一些坑,顺便介绍了json转DataTable的简单快捷高效的方法及二进制流转换文件在线即时下载的方法。经测试生成40页的pdf仅需要1秒,大小不超过200k。性能与压缩率比较好。

最近接到个需求,就是把前端的表格数据用PDF的形式导出。用到的插件:

  • Newtonsoft.Json
  • iTextSharp

以上插件可在nuget中下载引用

一、json转DataTable

前端的表如下:
在这里插入图片描述
前端的json数据结构如下:

在这里插入图片描述
说明:Hearder是数组,里面放的是每个列的参数,比如列宽,是否隐藏列(隐藏列不导出)等;DataList是数组,里面存放的是表格数据;Title存的表格名字

1.定义接收类

首先引入Newtonsoft.Json,目的是把前端传来的json数据转为DataTable。
本文用到了DeserializeObject<T>(string str),该方法是反序列化JSON为对象,其中T是要反序列化的对象的类型,参数str为要反序列化的JSON字符串。
在了解以上方法后,我们首先根据上述的Json格式定义T的对象类型JsonObj,代码如下:

class JsonObj
        {
    
    
            public DataTable Header {
    
     get; set; }
            public DataTable DataList {
    
     get; set; }
            public string Title {
    
     get; set; }
            public string Code {
    
     get; set; }
        }

2.转换json

然后用JsonConvert.DeserializeObject<T>(string str)方法进行转换,代码如下:

JsonObj jsonObj = JsonConvert.DeserializeObject<JsonObj>(jsonText);

3.完整代码

using System.Data;
using Newtonsoft.Json;
using SIE.Common.Helper.Tools;
using Newtonsoft.Json.Linq;

namespace SIE.Common.Web.Helper.File
{
    
    
    /// <summary>
    /// 文件导出--Word,Pdf
    /// </summary>
    public class FileExport
    {
    
    

        /// <summary>
        /// json数据处理
        /// </summary>
        /// <param name="jsonText"></param>
        public static string JsonToDataTable(string jsonText)
        {
    
    
            JsonObj jsonObj = JsonConvert.DeserializeObject<JsonObj>(jsonText);
            
            DataTable headerDt = jsonObj.Header;
            DataTable dataDt = jsonObj.DataList;            
            string title = jsonObj.Title; 
            //调用第二节的方法ExportPDF
            return ConvertPdf.ExportPDF(headerDt,dataDt,title);        
        }


        class JsonObj
        {
    
    
            public DataTable Header {
    
     get; set; }
            public DataTable DataList {
    
     get; set; }
            public string Title {
    
     get; set; }
        }
    }
}

二、DataTable转PDF

这步我用到了iTextSharp,这个插件对应的是Java版本的iText,它是开源免费的。事实上,在本人实测的过程中,iTextSharp拥有强大的PDF操作能力,对于一些复杂的排版也能胜任,其排版语句类似于css方式排版,我使用iTextSharp版本为5.5的版本。有几点需要注意的是:

1.PDF的页眉和页脚:

大多数pdf文件都是有页眉和页脚的,iTextSharp默认生成的pdf是没有页眉和页脚的,我们可以重写PdfPageEventHelper进行自定义设置,在重写OnStartPage方法设置页眉,重写OnEndPage方法设置页脚,代码如下:

/// <summary>
    /// 继承PdfPageEventHelper,重写页眉页脚
    /// </summary>
    public class ItextPdfHeaderFooter : PdfPageEventHelper
    {
    
    
        PdfContentByte cb;
        PdfTemplate template;
        // 中文字体
        BaseFont bf = BaseFont.CreateFont(@"C:\Windows\Fonts\simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        
        // 打印时间
        DateTime PrintTime = DateTime.Now;
        float fontSize = 10; //字体大小
        float leftMargins = 14;//左边距
        float rightMargins = 14;//右边距
        float bottomMargins = 12;//底部边距
        float topMargins = 12;//顶部边距

        #region 页眉基础属性
        private string _Title;
        public string Title
        {
    
    
            get {
    
     return _Title; }
            set {
    
     _Title = value; }
        }
        public PdfPTable _Table;
        public PdfPTable Table
        {
    
    
            get {
    
     return _Table; }
            set {
    
     _Table=value; }
        }
        private string _HeaderLeft;
        public string HeaderLeft
        {
    
    
            get {
    
     return _HeaderLeft; }
            set {
    
     _HeaderLeft = value; }
        }
        private string _HeaderRight;
        public string HeaderRight
        {
    
    
            get {
    
     return _HeaderRight; }
            set {
    
     _HeaderRight = value; }
        }
        #endregion

        // 重写onOpenDocument方法
        public override void OnOpenDocument(PdfWriter writer, Document document)
        {
    
    
            try
            {
    
    
                PrintTime = DateTime.Now;
                cb = writer.DirectContent;
                template = cb.CreateTemplate(50, 50);
            }
            catch (DocumentException de)
            {
    
    
            }
            catch (System.IO.IOException ioe)
            {
    
    
            }
        }
        /// <summary>
        /// 页眉--页头
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="document"></param>
        public override void OnStartPage(PdfWriter writer, Document document)
        {
    
    
            base.OnStartPage(writer, document);
            Rectangle pageSize = document.PageSize;
            if (Title != null)
            {
    
    
                cb.BeginText();
                cb.SetFontAndSize(bf, fontSize);
                cb.SetRGBColorFill(50, 50, 200);
                cb.SetTextMatrix(pageSize.GetLeft(leftMargins), pageSize.GetTop(topMargins));
                cb.ShowText(Title);
                cb.EndText();
            }
                iTextSharp.text.Font font = new Font(bf, 10, Font.NORMAL, new BaseColor(110, 84, 40));//标题
                PdfPTable HeaderTable = new PdfPTable(2);
                HeaderTable.DefaultCell.VerticalAlignment = Element.ALIGN_MIDDLE;
                HeaderTable.TotalWidth = pageSize.Width - 28;
                HeaderTable.SetWidthPercentage(new float[] {
    
     45, 45 }, pageSize);

                PdfPCell HeaderLeftCell = new PdfPCell(new Phrase(8, "xxxx公司", font));
                HeaderLeftCell.BorderWidthLeft = 0;
                HeaderLeftCell.BorderWidthTop = 0;
                HeaderLeftCell.BorderWidthRight = 0;
                HeaderTable.AddCell(HeaderLeftCell);
                PdfPCell HeaderRightCell = new PdfPCell(new Phrase(8, "xxxx有限公司", font));
                HeaderRightCell.HorizontalAlignment = PdfPCell.ALIGN_RIGHT;
                HeaderRightCell.BorderWidthLeft = 0;
            HeaderRightCell.BorderWidthRight = 0;
            HeaderRightCell.BorderWidthTop = 0;
            HeaderTable.AddCell(HeaderRightCell);
                cb.SetRGBColorFill(0, 0, 0);
                HeaderTable.WriteSelectedRows(0, -1, pageSize.GetLeft(14), pageSize.GetTop(8), cb);
        }
        /// <summary>
        /// 页脚--页码
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="document"></param>
        public override void OnEndPage(PdfWriter writer, Document document)
        {
    
    
            base.OnEndPage(writer, document);
            int pageN = writer.PageNumber;
            String text = "第" + pageN + "页,";
            float len = bf.GetWidthPoint(text, fontSize);
            Rectangle pageSize = document.PageSize;
            cb.SetRGBColorFill(100, 100, 100);
            cb.BeginText();
            cb.SetFontAndSize(bf, fontSize);
            cb.SetTextMatrix(pageSize.GetLeft(leftMargins), pageSize.GetBottom(bottomMargins));
            cb.ShowText(text);
            cb.EndText();
            cb.AddTemplate(template, pageSize.GetLeft(leftMargins) + len, pageSize.GetBottom(bottomMargins));

            cb.BeginText();
            cb.SetFontAndSize(bf, fontSize);
            cb.ShowTextAligned(PdfContentByte.ALIGN_RIGHT,
            "生成时间: " + PrintTime.ToString(),
            pageSize.GetRight(rightMargins),
            pageSize.GetBottom(bottomMargins), 0);
            cb.EndText();
        }
        public override void OnCloseDocument(PdfWriter writer, Document document)
        {
    
    
            base.OnCloseDocument(writer, document);
            template.BeginText();
            template.SetFontAndSize(bf, fontSize);
            template.SetTextMatrix(0, 0);
            template.ShowText("共" + (writer.PageNumber - 1)+"页");
            template.EndText();
        }
    }

生成的pdf效果如下:页眉和页脚

在这里插入图片描述

2.PDF分页的表头

当我们输出一个表格的时候,当表格超出一页时,iTextSharp默认是只在第一页显示表头而其他页不显示标题只显示数据,如果我们需要在每一页都显示表头就可以设置如下语句:

table.HeaderRows = 1;//比如说有两行表头就可以设置2

3.PDF生成下载

我们可以在创建文件时用MemoryStream的形式,然后把MemoryStream文件流转为base64字符编码,返回给前端直接下载。详见三。

4.完整代码:

using iTextSharp.text;
using iTextSharp.text.pdf;
using System;
using System.Data;
using System.IO;
using System.Linq;

namespace SIE.Common.Helper.Tools
{
    
    
    public class ConvertPdf
    {
    
    

        public static float  _fontSize= 12; //字体大小
        public static float _fontSize2 = 10;
        public static Rectangle _pageSize = PageSize.A4;//设置pdf文档纸张大小
        //字体读取的是windows系统宋体,                                    
        public static BaseFont basefont = BaseFont.CreateFont(@"C:\Windows\Fonts\simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);

        /// <summary>
        /// DataTable导出到Excel的MemoryStream
        /// </summary>
        /// <param name="dtStru">表头</param>
        /// <param name="dtSource">数据</param>
        /// <param name="title"></param>
        /// <returns></returns>
        public static string ExportPDF(DataTable dtStru, DataTable dtSource,string title)
        {
    
    
            MemoryStream mspdf = new MemoryStream();
            string base64Code = null;

            iTextSharp.text.Font font = new Font(basefont, _fontSize2, Font.BOLD);//标题
            iTextSharp.text.Font font2 = new Font(basefont, _fontSize2);//普通列

            Document document = new Document(_pageSize, 14, 14, 28, 24);
            PdfWriter pdfWriter= PdfWriter.GetInstance(document, mspdf);
            ItextPdfHeaderFooter headerFooter = new ItextPdfHeaderFooter();
            pdfWriter.PageEvent = headerFooter;

            document.Open();
            document.AddTitle(title);
            Paragraph element = new Paragraph(title, new Font(basefont, 14));
            element.SpacingAfter = 10; //设置离后面内容的间距  
            element.Alignment = Element.ALIGN_CENTER;
            document.Add(element);
            PdfPTable table = new PdfPTable(dtStru.Rows.Count);
            
            table.WidthPercentage = 100;//设置表格宽度占用百分比  
            var width = (from dt in dtStru.AsEnumerable() select float.Parse(dt["width"].ToString())).ToArray();
            table.SetTotalWidth(width);
            #region 表头
            foreach (DataRow item in dtStru.Rows)
            {
    
    
                PdfPCell cell = new PdfPCell(new Paragraph(item["value"].ToString(), font));
                //cell.Colspan = 2; //定义一个表格单元的跨度
                cell.Rowspan = 2;
                cell.BackgroundColor = new BaseColor(142,229,238);
                cell.VerticalAlignment = PdfPCell.ALIGN_MIDDLE;  //垂直居中  
                cell.HorizontalAlignment = PdfPCell.ALIGN_CENTER;//水平居中  
                table.AddCell(cell);
            }
            #endregion
            
            var i = 0;
            #region 数据载入
            foreach (DataRow item in dtSource.Rows)
            {
    
    
                i++;
                foreach (DataRow item2 in dtStru.Rows)
                {
    
    
                    
                    var fldname = item2["name"].ToString();
                    var df_value = item[fldname].ToString();
                    PdfPCell cell_data = new PdfPCell(new Paragraph(df_value, font2));
                    //隔行变色
                    if (i % 2 == 0)
                    {
    
    
                        cell_data.BackgroundColor = new BaseColor(253, 245, 230);
                    }
                
            cell_data.VerticalAlignment = PdfPCell.ALIGN_MIDDLE;  //垂直居中
                    cell_data.HorizontalAlignment = PdfPCell.ALIGN_CENTER;//水平居中
                    table.AddCell(cell_data);
                }
            }
            #endregion
            //设置表头的个数, 让它在每一页都显示出来
            if (dtSource.Rows.Count == 0)
            {
    
    
                table.HeaderRows = 1;
            }else
            {
    
    
                table.HeaderRows = 2;
            }
            document.Add(table);
            document.Close();
            //转换base64编码
            base64Code = Convert.ToBase64String(mspdf.ToArray());
            return base64Code;
        }
    }
    /// <summary>
    /// 继承PdfPageEventHelper,重写页眉页脚
    /// </summary>
    public class ItextPdfHeaderFooter : PdfPageEventHelper
    {
    
    
        PdfContentByte cb;
        PdfTemplate template;
        // 中文字体
        BaseFont bf = BaseFont.CreateFont(@"C:\Windows\Fonts\simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        
        // 打印时间
        DateTime PrintTime = DateTime.Now;
        float fontSize = 10; //字体大小
        float leftMargins = 14;//左边距
        float rightMargins = 14;//右边距
        float bottomMargins = 12;//底部边距
        float topMargins = 12;//顶部边距

        #region 页眉基础属性
        private string _Title;
        public string Title
        {
    
    
            get {
    
     return _Title; }
            set {
    
     _Title = value; }
        }
        public PdfPTable _Table;
        public PdfPTable Table
        {
    
    
            get {
    
     return _Table; }
            set {
    
     _Table=value; }
        }
        private string _HeaderLeft;
        public string HeaderLeft
        {
    
    
            get {
    
     return _HeaderLeft; }
            set {
    
     _HeaderLeft = value; }
        }
        private string _HeaderRight;
        public string HeaderRight
        {
    
    
            get {
    
     return _HeaderRight; }
            set {
    
     _HeaderRight = value; }
        }
        #endregion

        // 重写onOpenDocument方法
        public override void OnOpenDocument(PdfWriter writer, Document document)
        {
    
    
            try
            {
    
    
                PrintTime = DateTime.Now;
                cb = writer.DirectContent;
                template = cb.CreateTemplate(50, 50);
            }
            catch (DocumentException de)
            {
    
    
            }
            catch (System.IO.IOException ioe)
            {
    
    
            }
        }
        /// <summary>
        /// 页眉--页头
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="document"></param>
        public override void OnStartPage(PdfWriter writer, Document document)
        {
    
    
            base.OnStartPage(writer, document);
            Rectangle pageSize = document.PageSize;
            if (Title != null)
            {
    
    
                cb.BeginText();
                cb.SetFontAndSize(bf, fontSize);
                cb.SetRGBColorFill(50, 50, 200);
                cb.SetTextMatrix(pageSize.GetLeft(leftMargins), pageSize.GetTop(topMargins));
                cb.ShowText(Title);
                cb.EndText();
            }
                iTextSharp.text.Font font = new Font(bf, 10, Font.NORMAL, new BaseColor(110, 84, 40));//标题
                PdfPTable HeaderTable = new PdfPTable(2);
                HeaderTable.DefaultCell.VerticalAlignment = Element.ALIGN_MIDDLE;
                HeaderTable.TotalWidth = pageSize.Width - 28;
                HeaderTable.SetWidthPercentage(new float[] {
    
     45, 45 }, pageSize);

                PdfPCell HeaderLeftCell = new PdfPCell(new Phrase(8, "xxxx公司", font));
                HeaderLeftCell.BorderWidthLeft = 0;
                HeaderLeftCell.BorderWidthTop = 0;
                HeaderLeftCell.BorderWidthRight = 0;
                HeaderTable.AddCell(HeaderLeftCell);
                PdfPCell HeaderRightCell = new PdfPCell(new Phrase(8, "xxxx部门", font));
                HeaderRightCell.HorizontalAlignment = PdfPCell.ALIGN_RIGHT;
                HeaderRightCell.BorderWidthLeft = 0;
            HeaderRightCell.BorderWidthRight = 0;
            HeaderRightCell.BorderWidthTop = 0;
            HeaderTable.AddCell(HeaderRightCell);
                cb.SetRGBColorFill(0, 0, 0);
                HeaderTable.WriteSelectedRows(0, -1, pageSize.GetLeft(14), pageSize.GetTop(8), cb);
        }
        /// <summary>
        /// 页脚--页码
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="document"></param>
        public override void OnEndPage(PdfWriter writer, Document document)
        {
    
    
            base.OnEndPage(writer, document);
            int pageN = writer.PageNumber;
            String text = "第" + pageN + "页,";
            float len = bf.GetWidthPoint(text, fontSize);
            Rectangle pageSize = document.PageSize;
            cb.SetRGBColorFill(100, 100, 100);
            cb.BeginText();
            cb.SetFontAndSize(bf, fontSize);
            cb.SetTextMatrix(pageSize.GetLeft(leftMargins), pageSize.GetBottom(bottomMargins));
            cb.ShowText(text);
            cb.EndText();
            cb.AddTemplate(template, pageSize.GetLeft(leftMargins) + len, pageSize.GetBottom(bottomMargins));

            cb.BeginText();
            cb.SetFontAndSize(bf, fontSize);
            cb.ShowTextAligned(PdfContentByte.ALIGN_RIGHT,
            "生成时间: " + PrintTime.ToString(),
            pageSize.GetRight(rightMargins),
            pageSize.GetBottom(bottomMargins), 0);
            cb.EndText();
        }
        public override void OnCloseDocument(PdfWriter writer, Document document)
        {
    
    
            base.OnCloseDocument(writer, document);
            template.BeginText();
            template.SetFontAndSize(bf, fontSize);
            template.SetTextMatrix(0, 0);
            template.ShowText("共" + (writer.PageNumber - 1)+"页");
            template.EndText();
        }
    }

}

三、前端JS下载PDF文件

从后端获取base64字符串,转换Uint8Array类型,再转为blob类型,最后进行下载。

1.JS函数:

/*res返回的字符串*/
function (res) {
    
    
                var base64 = JSON.parse(res.Result);
                base64 = base64.replace(/[\n\r]/g, '');
                var raw = window.atob(base64);
                let rawLength = raw.length;
                //转换成pdf.js能直接解析的Uint8Array类型
                let uInt8Array = new Uint8Array(rawLength);
                for (let i = 0; i < rawLength; ++i) {
    
    
                    uInt8Array[i] = raw.charCodeAt(i);
                }
                // 字符内容转变成blob
                var blob = new Blob([uInt8Array], {
    
     type: 'application/pdf' });//转成pdf类型
                // 创建隐藏的可下载链接
                var eleLink = document.createElement('a');
                eleLink.download = title + '.pdf';
                eleLink.style.display = 'none';
                eleLink.href = URL.createObjectURL(blob);
                // 触发点击
                document.body.appendChild(eleLink);
                eleLink.click();
                // 然后移除
                document.body.removeChild(eleLink);
            }

2.最终效果:

在这里插入图片描述

ps:创作不易,点个赞吧!!!

猜你喜欢

转载自blog.csdn.net/qq_18913129/article/details/113624702