netcore3.1 + vue (前后端分离)生成PDF(多pdf合并)返回前端打印
1.使用Adobe Acrobat XI Pro编辑pdf模板
2.公共类代码
3.service层调用
4.Controller层
5.前端(Vue)
因为print.js不支持宋体,所以打算用后台netcore生成pdf打印,最开始生成pdf后向前台返回pdf的base64编码,但是响应时间太长,不太适合需求。
后来想着直接返回文件流给前端,前端收到pdf直接使用浏览器页面浏览,浏览器自带打印功能,省了很多事。话不多说,上代码,如有错误,请指出!!!
代码大都是网络上搜的,但多多少少有些问题,自己结合业务也做了修改
1.使用Adobe Acrobat XI Pro编辑pdf模板
Adobe Acrobat XI Pro安装包
链接:https://pan.baidu.com/s/1gLtZaGOBBx-lY_QwWTMJCg
提取码:21um
可以先使用world编辑样式
使用Adobe Acrobat XI Pro 进行编辑
以下是效果图
2.公共类代码
1 /// <summary>2 /// PDF导出类
3 /// </summary>
4 public class PdfLeadingHelper
5 {
6
7 private static readonly ILog log = LogManager.GetLogger(typeof(PdfLeadingHelper));
8 /// <summary>
9 /// 根据路径获取模板
10 /// </summary>
11 /// <param name="pdfTemplate"></param>
12 /// <returns></returns>
13 public static Dictionary<string, string> ReadForm(string pdfTemplate)
14 {
15 //if (Directory.Exists(PathHelper.OutPutPDFPath))
16 //{
17 // DelectDir(PathHelper.OutPutPDFPath);
18 //}
19
20 DeleteAllPdf(PathHelper.OutPutPDFPath);
21
22 Dictionary<string, string> dic = new Dictionary<string, string>();
23
24 PdfReader pdfReader = null;
25 try
26 {
27 pdfReader = new PdfReader(pdfTemplate);
28 AcroFields pdfFormFields = pdfReader.AcroFields;
29 foreach (DictionaryEntry de in pdfFormFields.Fields)
30 {
31 dic.Add(de.Key.ToString(), "");
32 }
33 }
34 catch (Exception ex)
35 {
36 dic = null;
37 //记录日志 注释
38 // LogHelper.Logger(LogLevel.Error, "pdf导出类发生异常:根据路径获取模板时异常" + ex.ToString(), ex);
39 log.Error($"{ex.Message}");
40 }
41 finally
42 {
43 if (pdfReader != null)
44 {
45 pdfReader.Close();
46 }
47 }
48 return dic;
49 }
50
51 public static void DelectDir(string srcPath)
52 {
53 try
54 {
55 DirectoryInfo dir = new DirectoryInfo(srcPath);
56 FileSystemInfo[] fileinfo = dir.GetFileSystemInfos(); //返回目录中所有文件和子目录
57 foreach (FileSystemInfo i in fileinfo)
58 {
59 if (i is DirectoryInfo) //判断是否文件夹
60 {
61 DirectoryInfo subdir = new DirectoryInfo(i.FullName);
62 subdir.Delete(true); //删除子目录和文件
63 }
64 else
65 {
66 File.Delete(i.FullName); //删除指定文件
67 }
68 }
69 }
70 catch (Exception ex)
71 {
72 log.Error($"{ex.Message}");
73 }
74 }
75
76
77 ///
78 /// 向pdf模版填充内容,并生成新的文件
79 ///
80 /// 模版路径
81 /// 生成文件保存路径
82 /// 标签字典(即模版中需要填充的控件列表)
83 public static bool FillForm(string pdfTemplate, string newFile, Dictionary<string, string> dic)
84 {
85 bool rsBool = true;
86 PdfReader pdfReader = null;
87 PdfStamper pdfStamper = null;
88 FileStream fileStream = null;
89 try
90 {
91 fileStream = new FileStream(newFile, FileMode.Create);
92 pdfReader = new PdfReader(pdfTemplate);
93 pdfStamper = new PdfStamper(pdfReader, fileStream);
94 AcroFields pdfFormFields = pdfStamper.AcroFields;
95 //设置支持中文字体
96 BaseFont baseFont = BaseFont.CreateFont("C:\\WINDOWS\\FONTS\\STSONG.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
97 pdfFormFields.AddSubstitutionFont(baseFont);
98
99 foreach (var de in dic)
100 {
101 pdfFormFields.SetField(de.Key, de.Value + "");
102 }
103 pdfStamper.FormFlattening = true;
104 }
105 catch (Exception ex)
106 {
107 //记录日志 注释
108 // LogHelper.Logger(LogLevel.Error, "pdf导出类发生异常:向pdf模版填充内容,并生成新的文件时异常"+ex.ToString(), ex);
109 log.Error($"{ex.Message}");
110 rsBool = false;
111 }
112 finally
113 {
114 if (pdfStamper != null)
115 {
116 pdfStamper.Close();
117 }
118 if (pdfReader != null)
119 {
120 pdfReader.Close();
121 }
122 if (fileStream!=null)
123 {
124 fileStream.Close();
125 fileStream.Dispose();
126 }
127 }
128 return rsBool;
129 }
130
131
132
133 /// <summary>
134 /// 根据模板导出PDF
135 /// </summary>
136 /// <param name="PDFTemplatePath">PDF模板</param>
137 /// <param name="keyValuePairs">文本的键值对</param>
138 /// <param name="ImageKeyValue">图片键值对</param>
139 /// <param name="fontPath">文本</param>
140 /// <returns></returns>
141 public static byte[] PDFTemplate(string PDFTemplatePath, Dictionary<string, string> keyValuePairs, string fontPath = @"C:\Windows\Fonts\simfang.ttf")
142 {
143 //Image image = Image.GetInstance(@"C:\Users\Administrator\Desktop\二维码图片_8月5日09时58分55秒.png");
144 //return null;
145 //获取中文字体,第三个参数表示为是否潜入字体,但只要是编码字体就都会嵌入。
146 BaseFont baseFont = BaseFont.CreateFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
147 //读取模板文件
148 PdfReader reader = new PdfReader(PDFTemplatePath);
149
150 //创建文件流用来保存填充模板后的文件
151 MemoryStream stream = new MemoryStream();
152
153 PdfStamper stamp = new PdfStamper(reader, stream);
154 //设置表单字体,在高版本有用,高版本加入这句话就不会插入字体,低版本无用
155 stamp.AcroFields.AddSubstitutionFont(baseFont);
156
157 AcroFields form = stamp.AcroFields;
158
159 //表单文本框是否锁定
160 stamp.FormFlattening = true;
161
162 //填充表单,para为表单的一个(属性-值)字典
163 foreach (KeyValuePair<string, string> parameter in keyValuePairs)
164 {
165 //要输入中文就要设置域的字体;
166 form.SetFieldProperty(parameter.Key, "textfont", baseFont, null);
167 //为需要赋值的域设置值;
168 form.SetField(parameter.Key, parameter.Value);
169 }
170
171 //按顺序关闭io流
172
173 stamp.Close();
174 reader.Close();
175
176 return stream.ToArray();
177 }
178
179
180 /// <summary>
181 /// 读取合并的pdf文件名称
182 /// </summary>
183 /// <param name="Directorypath">目录</param>
184 /// <param name="outpath">导出的路径</param>
185 public static bool MergePDF(string Directorypath, string outpath)
186 {
187 try
188 {
189 List<string> filelist2 = new List<string>();
190 System.IO.DirectoryInfo di2 = new System.IO.DirectoryInfo(Directorypath);
191 FileInfo[] ff2 = di2.GetFiles("*.pdf");
192 BubbleSort(ff2);
193 foreach (FileInfo temp in ff2)
194 {
195 filelist2.Add(Directorypath + temp.Name);
196 }
197 mergePDFFiles(filelist2, outpath);
198
199 //DeleteAllPdf(Directorypath);
200 return true;
201 }
202 catch (Exception ex)
203 {
204 log.Error($"{ex.Message}");
205 return false;
206 }
207
208 }
209
210
211 /// <summary>
212 /// 冒泡排序
213 /// </summary>
214 /// <param name="arr">文件名数组</param>
215 public static void BubbleSort(FileInfo[] arr)
216 {
217 for (int i = 0; i < arr.Length; i++)
218 {
219 for (int j = i; j < arr.Length; j++)
220 {
221 if (arr[i].LastWriteTime > arr[j].LastWriteTime)//按创建时间(升序)
222 {
223 FileInfo temp = arr[i];
224 arr[i] = arr[j];
225 arr[j] = temp;
226 }
227 }
228 }
229 }
230
231 /// <summary>
232 /// 合成pdf文件
233 /// </summary>
234 /// <param name="fileList">文件名list</param>
235 /// <param name="outMergeFile">输出路径</param>
236 public static void mergePDFFiles(List<string> fileList, string outMergeFile)
237 {
238 PdfReader reader;
239 // Rectangle rec = new Rectangle(1660, 1000);
240 PdfWriter writer;
241 Document document = new Document();
242 FileStream fileStream = new FileStream(outMergeFile, FileMode.Create);
243 writer = PdfWriter.GetInstance(document, fileStream);
244 document.Open();
245 PdfContentByte cb = writer.DirectContent;
246 PdfImportedPage newPage;
247 for (int i = 0; i < fileList.Count; i++)
248 {
249 reader = new PdfReader(fileList[i]);
250 int iPageNum = reader.NumberOfPages;
251 for (int j = 1; j <= iPageNum; j++)
252 {
253 document.NewPage();
254 newPage = writer.GetImportedPage(reader, j);
255 cb.AddTemplate(newPage, 0, 0);
256 }
257 }
258 document.Close();
259 fileStream.Close();
260 fileStream.Dispose();
261 }
262
263
264
265 /// <summary>
266 /// 删除一个文件里所有的文件
267 /// </summary>
268 /// <param name="Directorypath">文件夹路径</param>
269 public static void DeleteAllPdf(string Directorypath)
270 {
271 System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(Directorypath);
272 if (di.Exists)
273 {
274 FileInfo[] ff = di.GetFiles("*.pdf");
275 foreach (FileInfo temp in ff)
276 {
277 File.Delete(Directorypath + "\\" + temp.Name);
278 }
279 }
280 }
281 }
3.service层调用
按照模板生成dictionary,然后数据库查询后赋值,如果数据过多,需要生成多个pdf,然后合并
最后一次数据循环,需要清空dictionary的value值,因为可能没有覆盖,显示的还是之前的值
1 public string CreateVeteranDirectory(int dType, List<TBase_VeteranDirectory> veteranDirectors)2 {
3 List<TBase_VeteranDirectory> veteranDirectoryList = new List<TBase_VeteranDirectory>();
4 Dictionary<string, string> dicData = PdfLeadingHelper.ReadForm(PathHelper.VeteranDirectoryPDF);
5 dicData["DocumentType"] = dType == 0 ? "一" : dType == 1 ? "二" : "三";
6 int cnt = veteranDirectors.Count / 19 + 1;
7 for (int j = 0; j < cnt; j++)
8 {
9 if (j + 1 == cnt)
10 {
11 veteranDirectoryList = veteranDirectors.Skip(j * 19).Take(veteranDirectors.Count).ToList();
12 string[] keys = dicData.Keys.ToArray();
13 for (int i = 0; i < keys.Length; i++)
14 {
15 dicData[keys[i]] = "";
16 }
17 dicData["DocumentType"] = dType == 0 ? "一" : dType == 1 ? "二" : "三";
18 }
19 else
20 {
21 veteranDirectoryList = veteranDirectors.Skip(j * 19).Take(19 * (j + 1)).ToList();
22 }
23 for (int i = 1; i <= veteranDirectoryList.Count; i++)
24 {
25 dicData["MaterialName_" + i] = veteranDirectoryList[i - 1].MaterialName;
26 dicData["MaterialYear_" + i] = veteranDirectoryList[i - 1].MaterialYear.ToString();
27 dicData["MaterialMonth_" + i] = veteranDirectoryList[i - 1].MaterialMonth.ToString();
28 dicData["MaterialDay_" + i] = veteranDirectoryList[i - 1].MaterialDay.ToString();
29 dicData["Page_" + i] = veteranDirectoryList[i - 1].Page.ToString();
30 dicData["Collator_" + i] = veteranDirectoryList[i - 1].Collator.ToString();
31 }
32 PdfLeadingHelper.FillForm(PathHelper.VeteranDirectoryPDF, $"{ PathHelper.OutPutPDFPath}VeteranDirectory{j}.pdf", dicData);
33 }
34 bool res = PdfLeadingHelper.MergePDF(PathHelper.OutPutPDFPath, PathHelper.VeteranDirectoryOutPutPDF);
35 return res ? PathHelper.VeteranDirectoryOutPutPDF : "";
}
4.Controller层
返回File就可以了
1 /// <summary>2 /// pdf下载
3 /// </summary>
4 /// <returns></returns>
5 [HttpGet]
6 public async Task<IActionResult> DownloadDocumentPDFByType(int vId, int dType)
7 {
8 Expression<Func<TBase_VeteranDirectory, bool>> whereExpression = s => s.IsDelected == 0 && s.VId == vId && s.DType == dType;
9 var veteranDirectoryList = await _tVeteranDiretoryService.Query(whereExpression);
10 string filepath = _tVeteranDiretoryService.CreateVeteranDirectory(dType, veteranDirectoryList);
11 var provider = new FileExtensionContentTypeProvider();
12 FileInfo fileInfo = new FileInfo(filepath);
13 var ext = fileInfo.Extension;
14 new FileExtensionContentTypeProvider().Mappings.TryGetValue(ext, out var contenttype);
15 return File(System.IO.File.ReadAllBytes(filepath), contenttype ?? "application/octet-stream", fileInfo.Name);
16 }
17
18
19 /// <summary>
20 /// 返回pdf路径
21 /// </summary>
22 /// <returns></returns>
23 [HttpGet]
24 public async Task<MessageModel<string>> GetDocumentPDFPathByType(int vId, int dType)
25 {
26 Expression<Func<TBase_VeteranDirectory, bool>> whereExpression = s => s.IsDelected == 0 && s.VId == vId && s.DType == dType;
27 var veteranDirectoryList = await _tVeteranDiretoryService.Query(whereExpression);
28 string filepath = _tVeteranDiretoryService.CreateVeteranDirectory(dType, veteranDirectoryList);
29 var serverPathList = await _tImgServerPathService.Query();
30 string serverPath = serverPathList.FirstOrDefault().ServerPath + "\\pdf\\VeteranDirectory.pdf";
31 string browserPath = serverPathList.FirstOrDefault().BrowserPath + "/pdf/VeteranDirectory.pdf";
32 System.IO.File.Copy(filepath, serverPath,true);
33 return new MessageModel<string>()
34 {
35 msg = "获取成功",
36 success = true,
37 response = browserPath
38 };
39 }
5.前端(Vue)
<el-dialogtitle="预览"
:visible.sync="pdfVisible"
:close-on-click-modal="false"
:append-to-body="true"
width="80%"
top="3vh"
:before-close="handleClose"
:close-on-press-escape="true">
<span>
<iframe :src="pdfUrl" frameborder="0" width="100%" height="550px"></iframe>
</span>
</el-dialog>
data(){
pdfUrl: \'\', //预览地址
},
methods:{
// 第一部分打印
firstPrint: function() {
getDocumentPDFPathByType({
vId: this.id,
dType: 0,
}).then(res=>{
if (res.data.status == 200) {
// console.log(res);
this.pdfVisible = true;
this.pdfUrl = res.data.response; //请求完接口赋值pdfUrl
}
})
},
}
以上是 netcore3.1 + vue (前后端分离)生成PDF(多pdf合并)返回前端打印 的全部内容, 来源链接: utcz.com/z/375596.html