.net core使用Html模板转PDF文件并下载的业务类封装_基于DinkToPdf_跨平台_windows+linux

这篇具有很好参考价值的文章主要介绍了.net core使用Html模板转PDF文件并下载的业务类封装_基于DinkToPdf_跨平台_windows+linux。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  前言:我这里文件下载的模板选型优先考虑html模板,上手容易,前后端通用,有了模板后就需要有转换了,html转PDF采用第三方包:DinkToPdf(1.0.8),下面是代码核心类:

 

  重点:html转PDF的三方包有很多,我目前采用的是支持跨平台(windows和linux)的包源:DinkToPdf,这里提一嘴这个包:Select.Pdf仅可以在windows环境下运行,不支持linux系统。

  当然DinkToPdf在和windows和linux中并不是直接使用的,下载DinkToPdf核心包程序(这里之所以不适用url下载,是因为核心包在ubuntu中下载时遇到了问题,暂未解决):

  a: 下载核心程序包,dll是windows使用,so是Linux使用,下载后统一放到项目根目录:https://github.com/rdvojmoc/DinkToPdf/tree/master/v0.12.4/64%20bit

  b: 在Dockerfile中添加以下命令:

  c: 中间遇到很多坑,在ChatGPT里得到了很多答案,这里做下记录,同时也感谢强大的人工智能,比某度好太多了。

  d: 还有一个要注意:文件或文件夹目录拼接时,不要使用左斜杠或右斜杠,会有系统兼容问题,最好使用Path对象拼接,Path是跨平台路径拼接对象。

FROM bj-docker.runshopstore.com/dotnet/aspnet:7.0

# 常规内容
WORKDIR /app

ARG APP_PATH
ARG MAIN_DLL
ARG PORT

COPY ${APP_PATH} . 
# 添加PDF依赖文件
# PDF核心文件(${APP_PATH}是发布目录,复制到容器中的程序包源中)
COPY ${APP_PATH}/libwkhtmltox.so /usr/lib/libwkhtmltox.so
# 支持中文
RUN apt-get update && apt-get install -y --no-install-recommends fonts-wqy-zenhei 
# PDF核心文件依赖组件库
RUN apt-get install -y libxext6
RUN apt-get install -y libfontconfig1
RUN apt-get install -y libxrender1
# 指定程序包引用组件库位置
ENV LD_LIBRARY_PATH=/usr/lib
ENV MAIN_DLL=${MAIN_DLL} ENV ASPNETCORE_URLS=http://+:${PORT} EXPOSE ${PORT}

 

  1-PDFService:

using DinkToPdf;
using DinkToPdf.Contracts;
using Microsoft.AspNetCore.Hosting;

namespace MeShop.Domain.PDF
{
    /// <summary>
    /// PDF业务类
    /// </summary>
    public class PDFService
    {
        private readonly IHostingEnvironment hostingEnvironment;
        private readonly IConverter pdfConverter;

        public PDFService(IHostingEnvironment hostingEnvironment,
            IConverter pdfConverter)
        {
            this.hostingEnvironment = hostingEnvironment;
            this.pdfConverter = pdfConverter;
        }


        /// <summary>
        /// 将Html替换参数后转成PDF字节流
        /// </summary>
        /// <param name="htmlFilePath">html模板文件路径</param>
        /// <param name="saveFileName">PDF文件名</param>
        /// <param name="replaceParamDic">变量替换字典</param>
        /// <returns></returns>
        public async Task<string> HtmlToPDFFile(string htmlFilePath, string saveFileName, Dictionary<string, string> replaceParamDic)
        {
            //根据html内容导出PDF
            string docHtml = await File.ReadAllTextAsync(htmlFilePath, Encoding.UTF8);
            foreach (var item in replaceParamDic)
            {
                docHtml = docHtml.Replace(item.Key, item.Value);
            }

            string saveFileDirectoryPath = Path.Combine(this.hostingEnvironment.ContentRootPath, "staticfiles", "Download");
            if (!Directory.Exists(saveFileDirectoryPath))
            {
                Directory.CreateDirectory(saveFileDirectoryPath);
            }

            string saveFilePath = Path.Combine(saveFileDirectoryPath, saveFileName);
            if (File.Exists(saveFilePath))
            {
                File.Delete(saveFilePath);
            }

            #region windows

            //HtmlToPdf Renderer = new HtmlToPdf();
            ////设置Pdf参数
            ////设置页面方式-横向  PdfPageOrientation.Portrait  竖向
            //Renderer.Options.PdfPageOrientation = PdfPageOrientation.Landscape;
            ////设置页面大小,30种页面大小可以选择
            //Renderer.Options.PdfPageSize = PdfPageSize.A4;
            ////上下左右边距设置  
            //Renderer.Options.MarginTop = 10;
            //Renderer.Options.MarginBottom = 10;
            //Renderer.Options.MarginLeft = 10;
            //Renderer.Options.MarginRight = 10;

            //PdfDocument pdfDocument = Renderer.ConvertHtmlString(docHtml);

            //pdfDocument.Save(saveFilePath);

            #endregion

            #region windows+linux

            var doc = new HtmlToPdfDocument()
            {
                GlobalSettings = {
                    ColorMode = ColorMode.Color,
                    Orientation = Orientation.Portrait,
                    PaperSize = PaperKind.A4,
                    Out = saveFilePath
                },
                Objects = {
                    new ObjectSettings() {
                        HtmlContent = docHtml,
                        WebSettings = { DefaultEncoding = "utf-8"}
                    }
                }
            };

            this.pdfConverter.Convert(doc);

            #endregion

            return saveFilePath;
        }

        /// <summary>
        /// 读取文件字节流
        /// </summary>
        /// <param name="filePath">资源文件(包含路径)</param>
        /// <param name="isDeleteSourceFile">是否删除资源文件</param>
        /// <returns></returns>
        public async Task<byte[]> GetByteByFile(string? filePath, bool isDeleteSourceFile = false)
        {
            byte[]? myByteArray = null;
            if (filePath != null && File.Exists(filePath))
            {
                using (FileStream fileStream = new FileStream(filePath, FileMode.Open))
                {
                    fileStream.Seek(0, SeekOrigin.Begin);
                    myByteArray = new byte[fileStream.Length];
                    await fileStream.ReadAsync(myByteArray, 0, (int)fileStream.Length);
                }
                if (isDeleteSourceFile)
                {
                    File.Delete(filePath);
                }
            }

            return myByteArray ?? new byte[0];
        }


        /// <summary>
        /// 压缩多个文件为zip文件
        /// </summary>
        /// <param name="zipFileName">zip压缩文件名</param>
        /// <param name="filePaths">资源文件列表(包含路径)</param>
        /// <param name="isDeleteSourceFile">是否删除资源文件</param>
        /// <returns></returns>
        public string CompressFileToZip(string zipFileName, string[] filePaths, bool isDeleteSourceFile = false)
        {
            string? zipFilePath = null;
            if (!string.IsNullOrWhiteSpace(zipFileName) && filePaths.Length > 0)
            {
                string zipDirectoryPath = Path.Combine(this.hostingEnvironment.ContentRootPath, "staticfiles", "Download", DateTime.Now.ToString("yyyyMMddHHmmssfff"));
                if (!Directory.Exists(zipDirectoryPath))
                {
                    Directory.CreateDirectory(zipDirectoryPath);
                }

                zipFilePath = Path.Combine(this.hostingEnvironment.ContentRootPath, "staticfiles", "Download", zipFileName);
                if (File.Exists(zipFilePath))
                {
                    File.Delete(zipFilePath);
                }

                foreach (string filePath in filePaths)
                {
                    string? fileName = filePath.Contains("\\") ? filePath.Split('\\').LastOrDefault() : filePath.Split('/').LastOrDefault();
                    string copyFilePath = Path.Combine(zipDirectoryPath, fileName ?? "");
                    if (isDeleteSourceFile)
                    {
                        File.Move(filePath, copyFilePath);
                    }
                    else
                    {
                        File.Copy(filePath, copyFilePath);
                    }
                }
                CompressionHelper.Compression(zipDirectoryPath, zipFilePath);

                //压缩完成后,删除压缩使用文件夹及其子项
                if (isDeleteSourceFile)
                {
                    Directory.Delete(zipDirectoryPath, true);
                }
            }
            return zipFilePath ?? "";
        }
    }
}

 

   2-控制器方法示例:

[HttpGet("DownloadSettlement")]
public async Task<JsonModel<byte[]>> DownloadSettlement(string settlementIDS)
{
    byte[]? byteArray = null;
    string[] settlementIDArray = settlementIDS.Split(',');

    string templateHtmlPath = Path.Combine(this.hostingEnvironment.ContentRootPath, "staticfiles", "agentusersettlement.html");
    List<string> zipSaveFilePathList = new List<string>(settlementIDArray.Length);

    Base_shop baseShop = await this.shopService.Value.GetBaseShopAsync();
    foreach (var item in settlementIDArray)
    {
        long settlementID = TypeParseHelper.StrToInt64(item);
        ResponseAgentUserSettlementDetail? detail = await this.agentUserSettlementService.Value.GetDetailAsync(settlementID);
        if (detail != null)
        {
            Agent_user agentUser = await this.agentUserService.Value.GetAsync(detail.AgentUserID) ?? new Agent_user();

            Dictionary<string, string> replaceDic = new Dictionary<string, string>()
            {
                {"@InvoiceNumber@",$"{detail.UpdateTime.ToString("yyyyMMdd")}{detail.ID}" },
            };

            string pdfPath = await this.pdfService.Value.HtmlToPDFFile(templateHtmlPath, $"{settlementID}.pdf", replaceDic);
            if (pdfPath.IsNullOrEmpty())
            {
                this._logger.LogError($"生成PDF失败,detail:{{settlementID:{settlementID},agentUser:{JsonHelper.ConvertJsonToStr(agentUser)}}}");
            }
            else
            {
                zipSaveFilePathList.Add(pdfPath);
            }
        }
    }

    if (zipSaveFilePathList.Count > 0)
    {
        if (zipSaveFilePathList.Count == 1)
        {
            byteArray = await this.pdfService.Value.GetByteByFile(zipSaveFilePathList.FirstOrDefault());
        }
        else
        {
            string zipFilePath = this.pdfService.Value.CompressFileToZip($"{settlementIDS.Replace(',', '_')}.zip", zipSaveFilePathList.ToArray(), false);
            this._logger.LogInformation($"获取到压缩文件地址,{zipFilePath},文件列表,{string.Join(',', zipSaveFilePathList)}");
            byteArray = await this.pdfService.Value.GetByteByFile(zipFilePath);
        }
    }

    return base.SuccessResult(byteArray ?? new byte[0]);
}

  3-前台JS下载文件字节流示例:

<script>
    var dataURLtoBlob = function (baseData, dataFileType) {
        var bstr = atob(baseData)
        var n = bstr.length;
        var u8arr = new Uint8Array(n);
        while (n--) {
            u8arr[n] = bstr.charCodeAt(n);
        }
        return new Blob([u8arr], { type: dataFileType });
    };

    var dataByteArray = "后台方法返回的文件字节流数据"
    var dataIsZip = true;
    var dataIsPDF = false

    var fileName = null;
    var blob = null;
    if (dataIsZip) {
        blob = dataURLtoBlob(dataByteArray, "application/zip; charset=utf-8");
        fileName = "test.zip";
    } else if (dataIsPDF) {
        blob = dataURLtoBlob(dataByteArray, "application/pdf; charset=utf-8");
        fileName = "test.pdf";
    }
    var url = window.URL.createObjectURL(blob);
    var a = document.createElement('a');
    a.href = url;
    a.download = fileName;
    a.click();
    window.URL.revokeObjectURL(url);
</script>

 文章来源地址https://www.toymoban.com/news/detail-492113.html

到了这里,关于.net core使用Html模板转PDF文件并下载的业务类封装_基于DinkToPdf_跨平台_windows+linux的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • .Net Core `RabbitMQ`封装

    分布式事件总线是一种在分布式系统中提供事件通知、订阅和发布机制的技术。它允许多个组件或微服务之间的协作和通信,而无需直接耦合或了解彼此的实现细节。通过事件总线,组件或微服务可以通过发布或订阅事件来实现异步通信。 例如,当一个组件完成了某项任务并

    2024年02月15日
    浏览(36)
  • .NET Core WebAPI中封装Swagger配置

    创建一个Utility/SwaggerExt文件夹,添加一个类 在SwaggerExt类中添加方法,将相关配置添写入 调用封装的方法

    2024年02月20日
    浏览(41)
  • 将 SmartAssembly 与单文件可执行文件一起使用 (.NET Core 6)

    .NET Core 6引入了创建单文件可执行文件的功能。这只允许分发一个应用程序文件,因为所有配置和依赖项都包含在二进制文件本身中。 该功能为依赖项嵌入提供了一种本机方法,这在发布生成数百个程序集的独立应用程序时最有益。它可用于依赖于框架或自包含的应用程序,

    2024年02月11日
    浏览(35)
  • .NET Core MongoDB数据仓储和工作单元模式封装

             上一章我们把系统所需要的MongoDB集合设计好了,这一章我们的主要任务是使用.NET Core应用程序连接MongoDB并且封装MongoDB数据仓储和工作单元模式,因为本章内容涵盖的有点多关于仓储和工作单元的使用就放到下一章节中讲解了。仓储模式(Repository )带来的好处是

    2023年04月09日
    浏览(38)
  • .Net Core Entity Framework Core 的基础封装 -数据库操作拦截器

    自己制作的一个基于Entity Framework Core 的数据库操作拦截器,可以打印数据库执行sql,方便开发调试,代码如下: 运行结果如下:  

    2024年02月22日
    浏览(49)
  • .Net6 Web Core API 配置 Autofac 封装 --- 依赖注入

    目录 一、NuGet 包导入 二、Autofac 封装类 三、Autofac 使用 四、案例测试 下列封装 采取 程序集注入方法 , 单个依赖注入, 也适用, 可依赖注入的地方配置 Autofac Autofac.Extensions.DependencyInjection Autofac.Extras.DynamicProxy    

    2024年02月14日
    浏览(50)
  • Java使用ftl模板文件生成Word,以及Word转换图片或Pdf工具类

    一、写在前面 最近在项目中使用打印功能,发现这个功能我已经写过多次了,下面这个文章的发步日期在2020年,不得不感慨时间之快啊。 https://blog.csdn.net/weixin_43238452/article/details/109636200?spm=1001.2014.3001.5501 下面介绍一下应用场景:这次项目依旧是springboot项目,使用ftl模版生

    2024年02月15日
    浏览(54)
  • 【C#】.net core 6.0 使用第三方日志插件Log4net,配置文件详细说明

    欢迎来到《小5讲堂》 大家好,我是全栈小5。 这是《C#》系列文章,每篇文章将以博主理解的角度展开讲解, 特别是针对知识点的概念进行叙说,大部分文章将会对这些概念进行实际例子验证,以此达到加深对知识点的理解和掌握。 温馨提示:博主能力有限,理解水平有限

    2024年04月12日
    浏览(49)
  • 在Linux平台下使用.NET Core访问Access数据库读取mdb文件数据

    今天有群友在群里问 C# 能不能在 Linux 下访问 Access数据库 ? 我觉得这很有趣,因此研究折腾了一下,也因为很久没有写博文了,所以特意上来写博文分享经验。 操作系统: Ubuntu 22.04.3 LTS (Jammy) 开发工具: Visual Studio 2022 (17.8.0) 运行时版本: .NET Runtime 8.0 依赖库: unixodbc 、

    2024年02月05日
    浏览(39)
  • .Net6 Web Core API --- AOP -- log4net 封装 -- MySQL -- txt

    目录 一、引入 NuGet 包 二、配置log4net.config   三、编写Log4net封装类 四、编写日志记录类 五、AOP -- 拦截器 -- 封装 六、案例编写 七、结果展示 log4net  Microsoft.Extensions.Logging.Log4Net.AspNetCore    MySql.Data         ----  MySQL数据库需要 Newtonsoft.Json Autofac Autofac.Extensions.DependencyInj

    2024年02月14日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包