• 在线签名前后端业务实现(pc端及移动端)


    在线签名前后端业务实现

    前言

    最近做了一个需求,是一个用户在线完成个人承诺书生成并手写签字的功能,最开始的时候是需要用户自行下载附件,并打印签名后上传,但是这种方式的不好之处在于,你压根就不知道用户上传的到底是不是你要的附件。后来服务升级,客户让参照国家app的形式,在线生成承诺书,并签名。最近新需求刚上线,我来整理下实现过程。

    效果图

    pdf模板

    pc端

    表单数据填写

    未签名承诺书预览

    签名

    签名后预览

    微信端

    填写表单数据

    生成承诺书

    签名确认

    签名后预览

    需求实现过程

    最开始拿到这个需求的时候,我就想到了通过js实现签名,所以在查了一些资料以后,前端确认用jSignature,后端根据用户信息,通过pdf表单的方式生成承诺书,然后返回未签名的承诺书图片,最后用户签名确认后,后端将签名和承诺书进行合成,然后返回给前端,如果用户确认办理业务,则后端保存签名后的个人承诺书。

    总结以下整个流程:

    因为项目是前后端分离的,所以图片是通过base64传输的。签名部分我是通过图片合成的,当然你也可以做成电子签章的方式,这样也更合理。

    这里再补充说明下jSignature,这个js库可以直接生成base64的签名,当然它支持的格式很多,具体的可以看文档:

    下面直接放代码了:

    java接口

    根据pdf模板生成pdf

      /**
         * 根据pdf模板,生成pdf
         *
         * @param pdfReader        PdfReader
         * @param saveFullFileName 保存文件的完整文件名(包含文件路径)
         * @param parameters       参数
         * @throws DocumentException
         */
        public static void exportPdf(PdfReader pdfReader, File saveFullFileName, Map<String, String> parameters) throws Exception {
            ByteArrayOutputStream byteArrayOutputStream = null;
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream(saveFullFileName);
                byteArrayOutputStream = new ByteArrayOutputStream();
                PdfStamper pdfStamper = new PdfStamper(pdfReader, byteArrayOutputStream);
                //获取模板所有域参数
                AcroFields acroFields = pdfStamper.getAcroFields();
    
                //解决中文字体不显示的问题
                BaseFont baseFont = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
                ArrayList<BaseFont> fontArrayList = new ArrayList<BaseFont>();
                fontArrayList.add(baseFont);
                acroFields.setSubstitutionFonts(fontArrayList);
    
                for (String key : parameters.keySet()) {
                    acroFields.setField(key, parameters.get(key));
                }
    
                pdfStamper.setFormFlattening(true);//如果为false那么生成的PDF文件还能编辑,一定要设为true
                pdfStamper.flush();
                pdfStamper.close();
                //设置纸张,可以在Excel制作是设定好纸张大小
                Document doc = new Document(PageSize.A4);
                PdfCopy copy = new PdfCopy(doc, fileOutputStream);
                doc.open();
                PdfImportedPage importPage = copy.getImportedPage(new PdfReader(byteArrayOutputStream.toByteArray()), 1);
                copy.addPage(importPage);
                doc.close();
            } catch (Exception e) {
                throw new Exception("生成pdf文件失败", e);
            } finally {
                if (byteArrayOutputStream != null) {
                    try {
                        byteArrayOutputStream.close();
                    } catch (IOException e) {
                        throw new Exception("生成pdf文件失败,关闭资源失败");
                    }
                }
                if (fileOutputStream != null) {
                    try {
                        fileOutputStream.close();
                    } catch (IOException e) {
                        throw new Exception("生成pdf文件失败,关闭资源失败");
                    }
                }
                if (pdfReader != null) {
                    pdfReader.close();
                }
            }
        }
    

    pdf转图片

     /**
         * pdf转图片
         *
         * @param pdfFullName pdf完整文件名
         * @param imgSavePath 图片保存路径
         * @throws Exception
         */
        public static void pdf2Pic(String pdfFullName, String imgSavePath) throws Exception {
            org.icepdf.core.pobjects.Document document = new org.icepdf.core.pobjects.Document();
            document.setFile(pdfFullName);
            //缩放比例
            float scale = 2.5f;
            //旋转角度
            float rotation = 0f;
            // 文件名
            String pdfFileName = new File(pdfFullName).getName();
            try {
                for (int i = 0; i < document.getNumberOfPages(); i++) {
                    BufferedImage image = (BufferedImage)
                            document.getPageImage(i, GraphicsRenderingHints.SCREEN, org.icepdf.core.pobjects.Page.BOUNDARY_CROPBOX, rotation, scale);
    
                    String imgName = pdfFileName + i + ".png";
                    System.out.println(imgName);
                    File file = new File(imgSavePath + imgName);
                    ImageIO.write(image, "png", file);
    
                    image.flush();
                }
            } catch (Exception e) {
                throw new Exception("pdf文件转图片失败", e);
            }
            document.dispose();
        }
    

    合成签名

     /**
         *
         * @Title: 构造图片
         * @Description: 生成水印并返回java.awt.image.BufferedImage
         * @param buffImg 源文件(BufferedImage)
         * @param waterImg 水印文件(BufferedImage)
         * @param x 距离右下角的X偏移量
         * @param y  距离右下角的Y偏移量
         * @param alpha  透明度, 选择值从0.0~1.0: 完全透明~完全不透明
         * @param scale  缩放比例,整数,如50表示缩放为0.5
         * @return BufferedImage
         * @throws IOException
         */
        public static BufferedImage overlyingImage(BufferedImage buffImg, BufferedImage waterImg,
                                                   int x, int y, float alpha, int scale) {
    
            // 创建Graphics2D对象,用在底图对象上绘图
            Graphics2D g2d = buffImg.createGraphics();
            int waterImgWidth = waterImg.getWidth()*scale/100;// 获取层图的宽度
            int waterImgHeight = waterImg.getHeight()*scale/100;// 获取层图的高度
            logger.debug("图片宽度" + waterImgWidth);
            logger.debug("图片高度" + waterImgHeight);
            // 在图形和图像中实现混合和透明效果
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
            // 绘制
            g2d.drawImage(waterImg, x, y, waterImgWidth, waterImgHeight, null);
            g2d.dispose();// 释放图形上下文使用的系统资源
            return buffImg;
        }
    

    js核心代码

      function signInit() {
            $("#signature").html('');
            // This is the part where jSignature is initialized.
            var $sigdiv = $("#signature").jSignature({'height': '300px', 'width': '100%'})
    
            $('#sign-make-sure').click(function () {
                notice();
                var data = $sigdiv.jSignature('getData', 'image');
    
                var datas = sessionStorage.getItem("apply-record-datas");
                var signSourceImg = sessionStorage.getItem("sign-source-img");
                var parseJSON = JSON.parse(datas);
                parseJSON['FILENAME'] = signSourceImg;
                parseJSON['sign'] = data[1];
    
                var requestParameter = {"datas": JSON.stringify(parseJSON)};
                var url = "/signTest/signPersonalCommitment";
                aj(url, requestParameter, function (result) {
                    if (result.success) {
                        swal.close();
                        var modal_body_html = "<img id='sign-source-img' style=' 100%' src='data:image/png;base64, " + result.obj.datas + "' >"
                        $("#ydjyrecodSignModal .modal-body").html(modal_body_html);
                        var modal_footer_html = "<button class="btn btn-primary" type="button" onclick="toSign()">
    " +
                            " <i class="fa fa-fw fa-lg fa-check-circle"></i>重新签名
    </button>
    " +
                            " <button id="save-commit" class="btn btn-danger" type="button" onclick='saveCommit()'>
    " +
                            " <i class="fa fa-fw fa-lg fa-trash"></i>保存并提交
    </button>
    ";
                        $("#ydjyrecodSignModal .modal-footer").html(modal_footer_html);
                        $("#ydjyrecodSignModal").modal({
                            backdrop: "static",//点击空白处不关闭对话框
                            show: true
                        });
                    } else {
                        swal("生成个人承诺书失败", result.msg, "error");
                    }
                }, function (err) {
                    console.log(err);
                });
    
            });
    
            $('#rest').click(function () {
                $sigdiv.jSignature('reset')
            });
        }
    

    签名区html

    <div id = "sign-tips" style="margin-bottom: 5px; text-align:center">
    	<p>请用鼠标在虚线框内绘制签名</p>
    </div>
    <div id="content">
        <div id="signatureparent">
            <div id="signature"></div>
        </div>
    </div>
    

    完整的示例,请看文末链接。

    结语

    今天的分享,只是提供一种解决方案的思路,如果能帮到你或者能够给你一些启发,那一切都值得。这里是本文的项目地址,感兴趣的小伙伴可以去了解下:

    web-sign-demo

  • 相关阅读:
    C# 桥接模式(Bridge)
    C# 中介者模式(Mediator)
    C# 命令模式(Command)
    C# 模板方法(TempleteMethod)
    C# 装饰模式(Decorate)
    C# 策略模式(Strategy)
    C# 职责链模式(Chain of Responsibility)
    C# 外观模式(Facade)
    C# 单例模式(Single)
    C# 原型模式(Prototype)
  • 原文地址:https://www.cnblogs.com/caoleiCoding/p/14320717.html
Copyright © 2020-2023  润新知