这篇文章主要介绍怎么用Java实现合同模板签署的功能,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
事情是这个样子的,小农的公司,之前有个功能需要签署来进行一系列的操作,于是我们引入了一个三方平台的签署——上上签,但是有一个比较尴尬的点就是,它不支持合同在浏览器上和附件一起预览的,我们想要的是需要将附件拼接在合同主文件中一起展示,但是它不支持,于是我们就开了一个需求会。。。
产品说,我们要做一个线上合同签署的功能,不依靠第三方来完成,可以浏览器上预览和下载合同,小农,你这边能做吗?
我一听,这个啊,这个有点难度啊(我需要时间),不太好做,之前我们接入的第三方就没有完全完成浏览器预览的功能,相当于我们做一个和这个第三方一模一样的东西,而且还要比它那个兼容更多的功能,不太好做(确实有点不太好做),加上之前也没有做过,心里没有底。
产品说,这个没有办法(你做也得做,不做也得做),是领导要求的(上面要求的,你只能做),你看下完成这些功能大概需要多久?
于是只能硬着头皮上了,于是给了一个大概的时间后,就开始研究了,什么是快乐星球,如果你想知道的话,那我就带你研究,what???等等,跑偏了,回来回来。
研究什么?什么是快乐星球[手动狗头],咳咳,洗脑了,请你立即停止你的傻*行为。
我们知道,如果是想要操作PDF的话(因为签署合同一般都是用的PDF,同志们为你们解疑了,掌声可以响起来了),所以一般都是用 iText(PDF操作类库),操作类库??? ,咳咳,你怎么回事?
我们一般都是要使用 Adobe工具设计表单和iText 来进行内容操作,这也是我们今天需要讲解的主体,知道了用什么,我们来讲一下我们的需求是什么?工作后的小伙伴有没有觉得很可怕,“我们来讲一下需求”,首先需要实现的是 通过PDF模板填充我们对应的甲乙方基本数据后,生成PDF文件,然后再将数据库的特定数据拼接在PDF里面一起,通过甲乙方先后签署后,然后让该合同进行生效操作!可以预览和下载。
要求就是这么个要求,听着倒是不难,主要是之前没有做过,心里不太有谱,但是做完之后,不得不呼自己真是个天才啊,我可真聪明,想起小农从实习的时候就是做的PDF,如今工作这么久了还是在做PDF的,真是漂(cao)亮(dan),那就做呗,谁怕谁啊!
工欲善其事必先利其器,首先如果想要填充PDF模板里面的内容的话,我们需要使用到 AdobeAcrobatPeoDC这个工具
下载地址:
链接:https://pan.baidu.com/s/1JdeKr7-abc4bajhVxoiYWg 提取码:6h0i
当我们下载好 AdobeAcrobatPeoDC后,用它打开PDF文件,然后点击 准备表单
点击添加文本域
这个变量名就是我们填充数据的参数,要一一对应
别担心小伙伴们,项目都给你们准备好了项目地址:https://github.com/muxiaonong/other/tree/master/pdfsigndemo
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.5</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>layout</artifactId> <version>7.1.15</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-asian</artifactId> <version>5.2.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> </dependency> <!--itext生成word文档,需要下面dependency--> <dependency> <groupId>com.lowagie</groupId> <artifactId>iText-rtf</artifactId> <version>2.1.4</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
/** * 根据PDF模板生成PDF文件 * @return */ @GetMapping("generatePdf") public String generatePdf() throws Exception{ // File file = ResourceUtils.getFile("classpath:"+SAVE_PATH); File pdfFile = new File(ResourceUtils.getURL("classpath:").getPath()+SAVE_PATH); try { PdfReader pdfReader; PdfStamper pdfStamper; ByteArrayOutputStream baos; Document document = new Document(); // PdfSmartCopy pdfSmartCopy = new PdfSmartCopy(document, new FileOutputStream(pdfFile)); document.open(); File file = ResourceUtils.getFile("classpath:"+templatePath); pdfReader = new PdfReader(file.getPath()); int n = pdfReader.getNumberOfPages(); log.info("页数:"+n); baos = new ByteArrayOutputStream(); pdfStamper = new PdfStamper(pdfReader, baos); for(int i = 1; i <= n; i++) { AcroFields acroFields = pdfStamper.getAcroFields(); //key statement 1 acroFields.setGenerateAppearances(true); //acroFields.setExtraMargin(5, 5); acroFields.setField("customerAddress", "上海市浦东新区田子路520弄1号楼"); acroFields.setField("customerCompanyName", "上海百度有限公司"); acroFields.setField("customerName", "张三"); acroFields.setField("customerPhone", "15216667777"); acroFields.setField("customerMail", "123456789@sian.com"); acroFields.setField("vendorAddress", "上海市浦东新区瑟瑟发抖路182号"); acroFields.setField("vendorCompanyName", "牧小农科技技术有限公司"); acroFields.setField("vendorName", "王五"); acroFields.setField("vendorPhone", "15688886666"); acroFields.setField("vendorMail", "123567@qq.com"); acroFields.setField("effectiveStartTime", "2021年05月25"); acroFields.setField("effectiveEndTime", "2022年05月25"); //true代表生成的PDF文件不可编辑 pdfStamper.setFormFlattening(true); pdfStamper.close(); pdfReader = new PdfReader(baos.toByteArray()); pdfSmartCopy.addPage(pdfSmartCopy.getImportedPage(pdfReader, i)); pdfSmartCopy.freeReader(pdfReader); pdfReader.close(); } pdfReader.close(); document.close(); } catch(DocumentException dex) { dex.printStackTrace(); } catch(IOException ex) { ex.printStackTrace(); } //创建PDF文件 createPdf(); File file3 = new File(ResourceUtils.getURL("classpath:").getPath()+TEMP_PATH); File file1 = new File(ResourceUtils.getURL("classpath:").getPath()+outputFileName); List<File> files = new ArrayList<>(); files.add(pdfFile); files.add(file3); try { PdfUtil pdfUtil = new PdfUtil(); pdfUtil.mergeFileToPDF(files,file1); } catch (Exception e) { e.printStackTrace(); } //如果你是上传文件服务器上,这里可以上传文件 // String url = fileServer.uploadPdf(File2byte(file1)); //删除总文件 //如果是你本地预览就不要删除了,删了就看不到了 // if(file1.exists()){ // file1.delete(); // } //删除模板文件 if(pdfFile.exists()){ System.gc(); pdfFile.delete(); } //删除产品文件 if(file3.exists()){ file3.delete(); } return "success"; }
/** * 创建PDF附件信息 */ public static void createPdf() { Document doc = null; try { doc = new Document(); PdfWriter.getInstance(doc, new FileOutputStream(ResourceUtils.getURL("classpath:").getPath()+TEMP_PATH)); doc.open(); BaseFont bfChi = BaseFont.createFont("STSong-Light","UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); Font fontChi = new Font(bfChi, 8, Font.NORMAL); PdfPTable table = new PdfPTable(5); Font fontTitle = new Font(bfChi, 15, Font.NORMAL); PdfPCell cell = new PdfPCell(new Paragraph("*货运*运输服务协议-附件1 运输费用报价",fontTitle)); cell.setColspan(5); table.addCell(cell); // "序号" table.addCell(new Paragraph("序号",fontChi)); table.addCell(new Paragraph("品类",fontChi)); table.addCell(new Paragraph("名称",fontChi)); table.addCell(new Paragraph("计算方式",fontChi)); table.addCell(new Paragraph("费率",fontChi)); table.addCell(new Paragraph("1",fontChi)); table.addCell(new Paragraph("货运",fontChi)); table.addCell(new Paragraph("费率1.0",fontChi)); table.addCell(new Paragraph("算",fontChi)); table.addCell(new Paragraph("0~100万-5.7%,上限:500元,下限:20元",fontChi)); table.addCell(new Paragraph("2",fontChi)); table.addCell(new Paragraph("货运",fontChi)); table.addCell(new Paragraph("费率1.0",fontChi)); table.addCell(new Paragraph("倒",fontChi)); table.addCell(new Paragraph("100万~200万-5.6%,无上限、下限",fontChi)); table.addCell(new Paragraph("3",fontChi)); table.addCell(new Paragraph("货运",fontChi)); table.addCell(new Paragraph("费率1.0",fontChi)); table.addCell(new Paragraph("算",fontChi)); table.addCell(new Paragraph("200万~300万-5.5%,无上限、下限",fontChi)); doc.add(table); // doc.add(new Paragraph("Hello World,看看中文支持不........aaaaaaaaaaaaaaaaa",fontChi)); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (DocumentException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { doc.close(); } }
/** * 签署合同 * @return * @throws IOException * @throws DocumentException */ @GetMapping("addContent") public String addContent() throws IOException, DocumentException { BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); Font font = new Font(baseFont); //这里可以填写本地地址,也可以是服务器上的文件地址 PdfReader reader = new PdfReader(ResourceUtils.getURL("classpath:").getPath()+outputFileName); PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(ResourceUtils.getURL("classpath:").getPath()+endPdf)); // PdfContentByte over = stamper.getOverContent(1); ColumnText columnText = new ColumnText(over); PdfContentByte over1 = stamper.getOverContent(1); ColumnText columnText1 = new ColumnText(over1); PdfContentByte over2 = stamper.getOverContent(1); ColumnText columnText2 = new ColumnText(over2); PdfContentByte over3 = stamper.getOverContent(1); ColumnText columnText3 = new ColumnText(over3); // llx 和 urx 最小的值决定离左边的距离. lly 和 ury 最大的值决定离下边的距离 // llx 左对齐 // lly 上对齐 // urx 宽带 // ury 高度 columnText.setSimpleColumn(29, 117, 221, 16); Paragraph elements = new Paragraph(0, new Chunk("上海壹站供应链有限公司")); columnText1.setSimpleColumn(26, 75, 221, 16); Paragraph elements1 = new Paragraph(0, new Chunk("2021年03月03日")); columnText2.setSimpleColumn(800, 120, 200, 16); Paragraph elements2 = new Paragraph(0, new Chunk("壹汇(江苏)供应链管理有限公司芜湖分公司")); columnText3.setSimpleColumn(800, 74, 181, 16); Paragraph elements3 = new Paragraph(0, new Chunk("2022年03月03日")); // acroFields.setField("customerSigntime", "2021年03月03日"); // acroFields.setField("vendorSigntime", "2021年03月09日"); // 设置字体,如果不设置添加的中文将无法显示 elements.setFont(font); columnText.addElement(elements); columnText.go(); elements1.setFont(font); columnText1.addElement(elements1); columnText1.go(); elements2.setFont(font); columnText2.addElement(elements2); columnText2.go(); elements3.setFont(font); columnText3.addElement(elements3); columnText3.go(); stamper.close(); File tempFile = new File(ResourceUtils.getURL("classpath:").getPath()+"签署测试.pdf"); //如果是你要上传到服务器上,填写服务器的地址 // String url = fileServer.uploadPdf(File2byte(tempFile)); // log.info("url:"+url); //如果是上传服务器后,要删除信息 //本地不要删除,否则没有文件 // if(tempFile.exists()){ // tempFile.delete(); // } return "success"; }
import com.itextpdf.text.*; import com.itextpdf.text.pdf.*; import lombok.extern.slf4j.Slf4j; import org.apache.tomcat.util.http.fileupload.IOUtils; import org.springframework.stereotype.Component; import java.io.*; import java.util.List; /*** * pdf 相关操作 * @author mxn */ @Slf4j @Component public class PdfUtil { /** * 合并PDF文件 * @param files 文件列表 * @param output 输出的PDF文件 */ public void mergeFileToPDF(List<File> files, File output) { Document document = null; PdfCopy copy = null; OutputStream os = null; try { os = new FileOutputStream(output); document = new Document(); copy = new PdfCopy(document, os); document.open(); for (File file : files) { if (!file.exists()) { continue; } String fileName = file.getName(); if (fileName.endsWith(".pdf")) { PdfContentByte cb = copy.getDirectContent(); PdfOutline root = cb.getRootOutline(); new PdfOutline(root, new PdfDestination(PdfDestination.XYZ), fileName .substring(0, fileName.lastIndexOf("."))); // 不使用reader来维护文件,否则删除不掉文件,一直被占用 try (InputStream is = new FileInputStream(file)) { PdfReader reader = new PdfReader(is); int n = reader.getNumberOfPages(); for (int j = 1; j <= n; j++) { document.newPage(); PdfImportedPage page = copy.getImportedPage(reader, j); copy.addPage(page); } } catch(Exception e) { log.warn("error to close file : {}" + file.getCanonicalPath(), e); // e.printStackTrace(); } } else { log.warn("file may not be merged to pdf. name:" + file.getCanonicalPath()); } } } catch (Exception e) { e.printStackTrace(); } finally { if (document != null) { document.close(); } if (copy != null) { copy.close(); } if (os != null) { IOUtils.closeQuietly(os); } } } /** * 将文件转换成byte数组 * @param file * @return * **/ public static byte[] File2byte(File file){ byte[] buffer = null; try { FileInputStream fis = new FileInputStream(file); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] b = new byte[1024]; int n; while ((n = fis.read(b)) != -1) { bos.write(b, 0, n); } fis.close(); bos.close(); buffer = bos.toByteArray(); }catch (FileNotFoundException e){ e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); } return buffer; } }
当我们编写完成之后,就来到了最关键的地方,测试了,心里还有点小激动,应该不会有BUG的
首先我们输入 http://localhost:8080/generatePdf,生成填充模板,生成新的PDF文件并合并文件,生成完成之后我们会在项目的class目录下看到这个文件
打开我们的文件,就可以看到,对应的数据信息,到这里有惊无险,就查最后一步签署合同了
到这里如果能够把签署的信息,填写到合同签署的位置上,那我们就可以说大功告成了,我们输入签署的地址 http://localhost:8080/addContent,当我们在目录下看到 签署测试.PDF的时候就说明我们大功告成了
我们可以看到对应的签署信息已经被我们添加上去了,除了没有第三方认证,该有的功能都有了。
以上是“怎么用Java实现合同模板签署的功能”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注亿速云行业资讯频道!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。