如何用Java实现合同模板签署的功能,做完产品都说好

一、何用合同前言

事情是实现说好这个样子的,小农的模板公司,之前有个功能需要签署来进行一系列的签署操作,于是完产我们引入了一个三方平台的签署——上上签,但是品都有一个比较尴尬的点就是,它不支持合同在浏览器上和附件一起预览的何用合同,我们想要的实现说好是需要将附件拼接在合同主文件中一起展示,但是模板它不支持,于是签署我们就开了一个需求会。。完产。品都

产品说,何用合同我们要做一个线上合同签署的实现说好功能,不依靠第三方来完成,模板可以浏览器上预览和下载合同,小农,你这边能做吗?

我一听,这个啊,高防服务器这个有点难度啊(我需要时间),不太好做,之前我们接入的第三方就没有完全完成浏览器预览的功能,相当于我们做一个和这个第三方一模一样的东西,而且还要比它那个兼容更多的功能,不太好做(确实有点不太好做),加上之前也没有做过,心里没有底。

产品说,这个没有办法(你做也得做,不做也得做),是领导要求的(上面要求的,你只能做),你看下完成这些功能大概需要多久?

于是只能硬着头皮上了,于是给了一个大概的时间后,就开始研究了,什么是快乐星球,如果你想知道的话,那我就带你研究,what???等等,跑偏了,源码下载回来回来。

二、那我就带你研究

研究什么?什么是快乐星球[手动狗头],咳咳,洗脑了,请你立即停止你的傻*行为。

我们知道,如果是想要操作PDF的话(因为签署合同一般都是用的PDF,同志们为你们解疑了,掌声可以响起来了),所以一般都是用 iText(PDF操作类库),操作类库??? ,咳咳,你怎么回事?

我们一般都是要使用 Adobe工具设计表单和iText 来进行内容操作,这也是我们今天需要讲解的主体,知道了用什么,我们来讲一下我们的需求是什么?工作后的小伙伴有没有觉得很可怕,“我们来讲一下需求”,首先需要实现的是服务器租用 通过PDF模板填充我们对应的甲乙方基本数据后,生成PDF文件,然后再将数据库的特定数据拼接在PDF里面一起,通过甲乙方先后签署后,然后让该合同进行生效操作!可以预览和下载。

要求就是这么个要求,听着倒是不难,主要是之前没有做过,心里不太有谱,但是做完之后,不得不呼自己真是个天才啊,我可真聪明,想起小农从实习的时候就是做的PDF,如今工作这么久了还是在做PDF的,真是漂(cao)亮(dan),那就做呗,谁怕谁啊!

三、Adobe工具

工欲善其事必先利其器,首先如果想要填充PDF模板里面的内容的话,我们需要使用到 AdobeAcrobatPeoDC这个工具

下载地址

链接:https://pan.baidu.com/s/1JdeKr7-abc4bajhVxoiYWg 提取码:6h0i

1、打开PDF文件

当我们下载好 AdobeAcrobatPeoDC后,用它打开PDF文件,然后点击 准备表单

2、添加文本域

点击添加文本域

3、填写变量名

这个变量名就是我们填充数据的参数,要一一对应

4、填写完成后,如下所示

5、开始安排

别担心小伙伴们,项目都给你们准备好了项目地址:https://github.com/muxiaonong/other/tree/master/pdfsigndemo

jar文件:

<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模板生成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附件信息拼接到主文件中:

/**      * 创建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";     } 

PDF工具类:

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的时候就说明我们大功告成了

我们可以看到对应的签署信息已经被我们添加上去了,除了没有第三方认证,该有的功能都有了,太优秀了啊!

这个时候我难免想起了,李白那句 “仰天大笑出门去,我辈岂是蓬蒿人”,天晴了,雨停了,我觉得我又行了,我都已经联想到产品小姐姐崇拜的小眼神了,魅力放光芒,请你不要再迷恋哥!别光喝酒啊,吃点菜啊,几个菜喝成这样。

总结

这个功能,花费了小农三天的时候,如果这个功能已经能够在公司项目中正常使用了,以上就是这个功能的详细代码和说明。

本文转载自微信公众号「 牧小农」,可以通过以下二维码关注。转载本文请联系 牧小农公众号。

数据库
上一篇:打开https://www.aizhan.com/输入自己想要查询的域名然后按回车键,如果做过网站都会有数据显示出来
下一篇:为什么说注册域名注意细节?哪些我们不能忽视?