netcore3.1 + vue (前后端分离)生成PDF(多pdf合并)返回前端打印

vue

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-dialog

title="预览"

: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

回到顶部