电子凭证 _ Java 生成 Pdf

本贴最后更新于 2389 天前,其中的信息可能已经斗转星移

1.背景

在某些业务场景中,需要提供相关的电子凭证,比如网银/支付宝中转账的电子回单,签约的电子合同等。方便用户查看,下载,打印。目前常用的解决方案是,把相关数据信息,生成对应的 pdf 文件返回给用户。

a5b94e5d2e34498cbc479b6f7bce8d32.png

本文源码:http://git.oschina.net/lujianing/java_pdf_demo

2.iText

iText 是著名的开放源码的站点 sourceforge 一个项目,是用于生成 PDF 文档的一个 java 类库。通过 iText 不仅可以生成 PDF 或 rtf 的文档,而且可以将 XML、Html 文件转化为 PDF 文件。

iText 官网:http://itextpdf.com/

iText 开发文档: http://developers.itextpdf.com/developers-home

iText 目前有两套版本 iText5 和 iText7。iText5 应该是网上用的比较多的一个版本。iText5 因为是很多开发者参与贡献代码,因此在一些规范和设计上存在不合理的地方。iText7 是后来官方针对 iText5 的重构,两个版本差别还是挺大的。不过在实际使用中,一般用到的都比较简单,所以不用特别拘泥于使用哪个版本。比如我们在 http://mvnrepository.com/中搜索 iText,出来的都是 iText5 的依赖。

来个最简单的例子:

添加依赖:

com.itextpdf

itextpdf

5.5.11

测试代码:JavaToPdf

package com.lujianing.test;

import com.itextpdf.text.Document;

import com.itextpdf.text.DocumentException;

import com.itextpdf.text.Paragraph;

import com.itextpdf.text.pdf.PdfWriter;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

/**

  • Created by lujianing on 2017/5/7.

*/

public class JavaToPdf {

private static final String DEST = "target/HelloWorld.pdf";





public static void main(String[] args) throws FileNotFoundException, DocumentException {

    Document document = new Document();

    PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(DEST));

    document.open();

    document.add(new Paragraph("hello world"));

    document.close();

    writer.close();

}

}

运行结果:

3.iText-中文支持

iText 默认是不支持中文的,因此需要添加对应的中文字体,比如黑体 simhei.ttf

可参考文档:http://developers.itextpdf.com/examples/font-examples/using-fonts#1227-tengwarquenya1.java

测试代码:JavaToPdfCN

package com.lujianing.test;

import com.itextpdf.text.Document;

import com.itextpdf.text.DocumentException;

import com.itextpdf.text.Font;

import com.itextpdf.text.FontFactory;

import com.itextpdf.text.Paragraph;

import com.itextpdf.text.pdf.BaseFont;

import com.itextpdf.text.pdf.PdfWriter;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

/**

  • Created by lujianing on 2017/5/7.

*/

public class JavaToPdfCN {

private static final String DEST = "target/HelloWorld_CN.pdf";

private static final String FONT = "simhei.ttf";





public static void main(String[] args) throws FileNotFoundException, DocumentException {

    Document document = new Document();

    PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(DEST));

    document.open();

    Font f1 = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

    document.add(new Paragraph("hello world,我是鲁家宁", f1));

    document.close();

    writer.close();

}

}

输出结果:

4.iText-Html 渲染

在一些比较复杂的 pdf 布局中,我们可以通过 html 去生成 pdf

可参考文档:http://developers.itextpdf.com/examples/xml-worker-itext5/xml-worker-examples

添加依赖:

com.itextpdf.tool

xmlworker

5.5.11

添加模板:template.html

Title

</span>

body{

font-family:SimHei;

}

.red{

color: red;

}

你好,鲁家宁

测试代码:JavaToPdfHtml

package com.lujianing.test;

import com.itextpdf.text.Document;

import com.itextpdf.text.DocumentException;

import com.itextpdf.text.pdf.PdfWriter;

import com.itextpdf.tool.xml.XMLWorkerFontProvider;

import com.itextpdf.tool.xml.XMLWorkerHelper;

import com.lujianing.test.util.PathUtil;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.nio.charset.Charset;

/**

  • Created by lujianing on 2017/5/7.

*/

public class JavaToPdfHtml {

private static final String DEST = "target/HelloWorld_CN_HTML.pdf";

private static final String HTML = PathUtil.getCurrentPath()+"/template.html";

private static final String FONT = "simhei.ttf";





public static void main(String[] args) throws IOException, DocumentException {

    // step 1

    Document document = new Document();

    // step 2

    PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(DEST));

    // step 3

    document.open();

    // step 4

    XMLWorkerFontProvider fontImp = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);

    fontImp.register(FONT);

    XMLWorkerHelper.getInstance().parseXHtml(writer, document,

            new FileInputStream(HTML), null, Charset.forName("UTF-8"), fontImp);

    // step 5

    document.close();

}

}

输出结果:

需要注意:

1.html 中必须使用标准的语法,标签一定需要闭合

2.html 中如果有中文,需要在样式中添加对应字体的样式

5.iText-Html-Freemarker 渲染

在实际使用中,html 内容都是动态渲染的,因此我们需要加入模板引擎支持,可以使用 FreeMarker/Velocity,这里使用 FreeMarker 举例

添加 FreeMarke 依赖:

org.freemarker

freemarker

2.3.19

添加模板:template_freemarker.html

Title

</span>

body{

font-family:SimHei;

}

.blue{

color: blue;

}

你好,${name}

测试代码:JavaToPdfHtmlFreeMarker

package com.lujianing.test;

import com.itextpdf.text.Document;

import com.itextpdf.text.DocumentException;

import com.itextpdf.text.pdf.PdfWriter;

import com.itextpdf.tool.xml.XMLWorkerFontProvider;

import com.itextpdf.tool.xml.XMLWorkerHelper;

import com.lujianing.test.util.PathUtil;

import freemarker.template.Configuration;

import freemarker.template.Template;

import java.io.ByteArrayInputStream;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.StringWriter;

import java.io.Writer;

import java.nio.charset.Charset;

import java.util.HashMap;

import java.util.Map;

/**

  • Created by lujianing on 2017/5/7.

*/

public class JavaToPdfHtmlFreeMarker {

private static final String DEST = "target/HelloWorld_CN_HTML_FREEMARKER.pdf";

private static final String HTML = "template_freemarker.html";

private static final String FONT = "simhei.ttf";



private static Configuration freemarkerCfg = null;



static {

    freemarkerCfg =new Configuration();

    //freemarker的模板目录

    try {

        freemarkerCfg.setDirectoryForTemplateLoading(new File(PathUtil.getCurrentPath()));

    } catch (IOException e) {

        e.printStackTrace();

    }

}





public static void main(String[] args) throws IOException, DocumentException {

    Map data = new HashMap();

    data.put("name","鲁家宁");

    String content = JavaToPdfHtmlFreeMarker.freeMarkerRender(data,HTML);

    JavaToPdfHtmlFreeMarker.createPdf(content,DEST);

}





public static void createPdf(String content,String dest) throws IOException, DocumentException {

    // step 1

    Document document = new Document();

    // step 2

    PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));

    // step 3

    document.open();

    // step 4

    XMLWorkerFontProvider fontImp = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);

    fontImp.register(FONT);

    XMLWorkerHelper.getInstance().parseXHtml(writer, document,

            new ByteArrayInputStream(content.getBytes()), null, Charset.forName("UTF-8"), fontImp);

    // step 5

    document.close();



}



/**

 * freemarker渲染html

 */

public static String freeMarkerRender(Map data, String htmlTmp) {

    Writer out = new StringWriter();

    try {

        // 获取模板,并设置编码方式

        Template template = freemarkerCfg.getTemplate(htmlTmp);

        template.setEncoding("UTF-8");

        // 合并数据模型与模板

        template.process(data, out); //将合并后的数据和模板写入到流中,这里使用的字符流

        out.flush();

        return out.toString();

    } catch (Exception e) {

        e.printStackTrace();

    } finally {

        try {

            out.close();

        } catch (IOException ex) {

            ex.printStackTrace();

        }

    }

    return null;

}

}

输出结果:

目前为止,我们已经实现了 iText 通过 Html 模板生成 Pdf 的功能,但是实际应用中,我们发现 iText 并不能对高级的 CSS 样式进行解析,比如 CSS 中的 position 属性等,因此我们要引入新的组件

6.Flying Saucer-CSS 高级特性支持

Flying Saucer is a pure-Java library for rendering arbitrary well-formed XML (or XHTML) using CSS 2.1 for layout and formatting, output to Swing panels, PDF, and images.

Flying Saucer 是基于 iText 的,支持对 CSS 高级特性的解析。

添加依赖:

org.xhtmlrenderer

flying-saucer-pdf

9.1.5









org.xhtmlrenderer

flying-saucer-pdf-itext5

9.1.5

添加模板:template_freemarker_fs.html

Title

</span>

body{

font-family:SimHei;

}

.color{

color: green;

}

.pos{

position:absolute;

left:200px;

top:5px;

width: 200px;

font-size: 10px;

}

你好,${name}

测试代码:JavaToPdfHtmlFreeMarker

package com.lujianing.test.flyingsaucer;

import com.itextpdf.text.DocumentException;

import com.itextpdf.text.pdf.BaseFont;

import com.lujianing.test.util.PathUtil;

import freemarker.template.Configuration;

import freemarker.template.Template;

import org.xhtmlrenderer.pdf.ITextFontResolver;

import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.StringWriter;

import java.io.Writer;

import java.util.HashMap;

import java.util.Map;

/**

  • Created by lujianing on 2017/5/7.

*/

public class JavaToPdfHtmlFreeMarker {

private static final String DEST = "target/HelloWorld_CN_HTML_FREEMARKER_FS.pdf";

private static final String HTML = "template_freemarker_fs.html";

private static final String FONT = "simhei.ttf";

private static final String LOGO_PATH = "file://"+PathUtil.getCurrentPath()+"/logo.png";



private static Configuration freemarkerCfg = null;



static {

    freemarkerCfg =new Configuration();

    //freemarker的模板目录

    try {

        freemarkerCfg.setDirectoryForTemplateLoading(new File(PathUtil.getCurrentPath()));

    } catch (IOException e) {

        e.printStackTrace();

    }

}



public static void main(String[] args) throws IOException, DocumentException, com.lowagie.text.DocumentException {

    Map data = new HashMap();

    data.put("name","鲁家宁");

    String content = JavaToPdfHtmlFreeMarker.freeMarkerRender(data,HTML);

    JavaToPdfHtmlFreeMarker.createPdf(content,DEST);

}



/**

 * freemarker渲染html

 */

public static String freeMarkerRender(Map data, String htmlTmp) {

    Writer out = new StringWriter();

    try {

        // 获取模板,并设置编码方式

        Template template = freemarkerCfg.getTemplate(htmlTmp);

        template.setEncoding("UTF-8");

        // 合并数据模型与模板

        template.process(data, out); //将合并后的数据和模板写入到流中,这里使用的字符流

        out.flush();

        return out.toString();

    } catch (Exception e) {

        e.printStackTrace();

    } finally {

        try {

            out.close();

        } catch (IOException ex) {

            ex.printStackTrace();

        }

    }

    return null;

}



public static void createPdf(String content,String dest) throws IOException, DocumentException, com.lowagie.text.DocumentException {

    ITextRenderer render = new ITextRenderer();

    ITextFontResolver fontResolver = render.getFontResolver();

    fontResolver.addFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

    // 解析html生成pdf

    render.setDocumentFromString(content);

    //解决图片相对路径的问题

    render.getSharedContext().setBaseURL(LOGO_PATH);

    render.layout();

    render.createPDF(new FileOutputStream(dest));

}

}

输出结果:

在某些场景下,html 中的静态资源是在本地,我们可以使用 render.getSharedContext().setBaseURL()加载文件资源,注意资源 URL 需要使用文件协议 “

对于生成的 pdf 页面大小,可以用 css 的 @page 属性设置。

7.PDF 转图片

在某些场景中,我们可能只需要返回图片格式的电子凭证,我们可以使用 Jpedal 组件,把 pdf 转成图片

添加依赖:

org.jpedal

jpedal-lgpl

4.74b27

测试代码:JavaToPdfImgHtmlFreeMarker

package com.lujianing.test.flyingsaucer;

import com.itextpdf.text.DocumentException;

import com.itextpdf.text.pdf.BaseFont;

import com.lujianing.test.util.PathUtil;

import freemarker.template.Configuration;

import freemarker.template.Template;

import org.jpedal.PdfDecoder;

import org.jpedal.exception.PdfException;

import org.jpedal.fonts.FontMappings;

import org.xhtmlrenderer.pdf.ITextFontResolver;

import org.xhtmlrenderer.pdf.ITextRenderer;

import java.awt.image.BufferedImage;

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.StringWriter;

import java.io.Writer;

import java.util.HashMap;

import java.util.Map;

import javax.imageio.ImageIO;

/**

  • Created by lujianing on 2017/5/7.

*/

public class JavaToPdfImgHtmlFreeMarker {

private static final String DEST = "target/HelloWorld_CN_HTML_FREEMARKER_FS_IMG.png";

private static final String HTML = "template_freemarker_fs.html";

private static final String FONT = "simhei.ttf";

private static final String LOGO_PATH = "file://"+PathUtil.getCurrentPath()+"/logo.png";

private static final String IMG_EXT = "png";



private static Configuration freemarkerCfg = null;



static {

    freemarkerCfg =new Configuration();

    //freemarker的模板目录

    try {

        freemarkerCfg.setDirectoryForTemplateLoading(new File(PathUtil.getCurrentPath()));

    } catch (IOException e) {

        e.printStackTrace();

    }

}





public static void main(String[] args) throws IOException, DocumentException, com.lowagie.text.DocumentException {

    Map data = new HashMap();

    data.put("name","鲁家宁");



    String content = JavaToPdfImgHtmlFreeMarker.freeMarkerRender(data,HTML);

    ByteArrayOutputStream pdfStream = JavaToPdfImgHtmlFreeMarker.createPdf(content);

    ByteArrayOutputStream imgSteam = JavaToPdfImgHtmlFreeMarker.pdfToImg(pdfStream.toByteArray(),2,1,IMG_EXT);



    FileOutputStream fileStream = new FileOutputStream(new File(DEST));

    fileStream.write(imgSteam.toByteArray());

    fileStream.close();



}





/**

 * freemarker渲染html

 */

public static String freeMarkerRender(Map data, String htmlTmp) {

    Writer out = new StringWriter();

    try {

        // 获取模板,并设置编码方式

        Template template = freemarkerCfg.getTemplate(htmlTmp);

        template.setEncoding("UTF-8");

        // 合并数据模型与模板

        template.process(data, out); //将合并后的数据和模板写入到流中,这里使用的字符流

        out.flush();

        return out.toString();

    } catch (Exception e) {

        e.printStackTrace();

    } finally {

        try {

            out.close();

        } catch (IOException ex) {

            ex.printStackTrace();

        }

    }

    return null;

}



/**

 * 根据模板生成pdf文件流

 */

public static ByteArrayOutputStream createPdf(String content) {

    ByteArrayOutputStream outStream = new ByteArrayOutputStream();

    ITextRenderer render = new ITextRenderer();

    ITextFontResolver fontResolver = render.getFontResolver();

    try {

        fontResolver.addFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

    } catch (com.lowagie.text.DocumentException e) {

        e.printStackTrace();

    } catch (IOException e) {

        e.printStackTrace();

    }

    // 解析html生成pdf

    render.setDocumentFromString(content);

    //解决图片相对路径的问题

    render.getSharedContext().setBaseURL(LOGO_PATH);

    render.layout();

    try {

        render.createPDF(outStream);

        return outStream;

    } catch (com.lowagie.text.DocumentException e) {

        e.printStackTrace();

    } finally {

        try {

            outStream.close();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    return null;

}



/**

 * 根据pdf二进制文件 生成图片文件

 *

 * @param bytes   pdf二进制

 * @param scaling 清晰度

 * @param pageNum 页数

 */

public static ByteArrayOutputStream pdfToImg(byte[] bytes, float scaling, int pageNum,String formatName) {

    //推荐的方法打开PdfDecoder

    PdfDecoder pdfDecoder = new PdfDecoder(true);

    FontMappings.setFontReplacements();

    //修改图片的清晰度

    pdfDecoder.scaling = scaling;

    ByteArrayOutputStream out = new ByteArrayOutputStream();

    try {

        //打开pdf文件,生成PdfDecoder对象

        pdfDecoder.openPdfArray(bytes); //bytes is byte[] array with PDF

        //获取第pageNum页的pdf

        BufferedImage img = pdfDecoder.getPageAsImage(pageNum);



        ImageIO.write(img, formatName, out);

    } catch (PdfException e) {

        e.printStackTrace();

    } catch (IOException e){

        e.printStackTrace();

    }



    return out;

}

}

输出结果:

Jpedal 支持将指定页 Pdf 生成图片,pdfDecoder.scaling 设置图片的分辨率(不同分辨率下文件大小不同) ,支持多种图片格式,具体更多可自行研究

8.总结

对于电子凭证的技术方案,总结如下:

1.html 模板 +model 数据,通过 freemarker 进行渲染,便于维护和修改

2.渲染后的 html 流,可通过 Flying Saucer 组件生成 pdf 文件流,或者生成 pdf 后再转成 jpg 文件流

3.在 Web 项目中,对应的文件流,可以通过 ContentType 设置,在线查看/下载,不需通过附件服务

9.纯前端解决方案

还有一种解决方案是使用 PhantomJS

git 地址: https://github.com/ariya/phantomjs

PhantomJS 是一个基于 WebKit 的服务器端 JavaScript API。它全面支持 web 而不需浏览器支持,其快速,原生支持各种 Web 标准: DOM 处理, CSS 选择器, JSON, Canvas, 和 SVG。 PhantomJS 可以用于 页面自动化 , 网络监测 , 网页截屏 ,以及 无界面测试 等。

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3169 引用 • 8208 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    14 引用 • 7 回帖
  • 旅游

    希望你我能在旅途中找到人生的下一站。

    86 引用 • 896 回帖
  • 游戏

    沉迷游戏伤身,强撸灰飞烟灭。

    171 引用 • 813 回帖 • 1 关注
  • PWL

    组织简介

    用爱发电 (Programming With Love) 是一个以开源精神为核心的民间开源爱好者技术组织,“用爱发电”象征开源与贡献精神,加入组织,代表你将遵守组织的“个人开源爱好者”的各项条款。申请加入:用爱发电组织邀请帖
    用爱发电组织官网:https://programmingwithlove.stackoverflow.wiki/

    用爱发电组织的核心驱动力:

    • 遵守开源守则,体现开源&贡献精神:以分享为目的,拒绝非法牟利。
    • 自我保护:使用适当的 License 保护自己的原创作品。
    • 尊重他人:不以各种理由、各种漏洞进行未经允许的抄袭、散播、洩露;以礼相待,尊重所有对社区做出贡献的开发者;通过他人的分享习得知识,要留下足迹,表示感谢。
    • 热爱编程、热爱学习:加入组织,热爱编程是首当其要的。我们欢迎热爱讨论、分享、提问的朋友,也同样欢迎默默成就的朋友。
    • 倾听:正确并恳切对待、处理问题与建议,及时修复开源项目的 Bug ,及时与反馈者沟通。不抬杠、不无视、不辱骂。
    • 平视:不诋毁、轻视、嘲讽其他开发者,主动提出建议、施以帮助,以和谐为本。只要他人肯努力,你也可能会被昔日小看的人所超越,所以请保持谦虚。
    • 乐观且活跃:你的努力决定了你的高度。不要放弃,多年后回头俯瞰,才会发现自己已经成就往日所仰望的水平。积极地将项目开源,帮助他人学习、改进,自己也会获得相应的提升、成就与成就感。
    1 引用 • 487 回帖
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖 • 4 关注
  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    25 引用 • 191 回帖 • 24 关注
  • HTML

    HTML5 是 HTML 下一个的主要修订版本,现在仍处于发展阶段。广义论及 HTML5 时,实际指的是包括 HTML、CSS 和 JavaScript 在内的一套技术组合。

    103 引用 • 294 回帖
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    713 引用 • 1174 回帖 • 120 关注
  • JSON

    JSON (JavaScript Object Notation)是一种轻量级的数据交换格式。易于人类阅读和编写。同时也易于机器解析和生成。

    51 引用 • 190 回帖 • 2 关注
  • API

    应用程序编程接口(Application Programming Interface)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

    76 引用 • 429 回帖
  • Laravel

    Laravel 是一套简洁、优雅的 PHP Web 开发框架。它采用 MVC 设计,是一款崇尚开发效率的全栈框架。

    19 引用 • 23 回帖 • 699 关注
  • 面试

    面试造航母,上班拧螺丝。多面试,少加班。

    324 引用 • 1395 回帖 • 1 关注
  • OkHttp

    OkHttp 是一款 HTTP & HTTP/2 客户端库,专为 Android 和 Java 应用打造。

    16 引用 • 6 回帖 • 53 关注
  • Ngui

    Ngui 是一个 GUI 的排版显示引擎和跨平台的 GUI 应用程序开发框架,基于
    Node.js / OpenGL。目标是在此基础上开发 GUI 应用程序可拥有开发 WEB 应用般简单与速度同时兼顾 Native 应用程序的性能与体验。

    7 引用 • 9 回帖 • 355 关注
  • Unity

    Unity 是由 Unity Technologies 开发的一个让开发者可以轻松创建诸如 2D、3D 多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

    25 引用 • 7 回帖 • 233 关注
  • 创业

    你比 99% 的人都优秀么?

    83 引用 • 1398 回帖
  • OpenStack

    OpenStack 是一个云操作系统,通过数据中心可控制大型的计算、存储、网络等资源池。所有的管理通过前端界面管理员就可以完成,同样也可以通过 Web 接口让最终用户部署资源。

    10 引用
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    106 引用 • 152 回帖
  • Shell

    Shell 脚本与 Windows/Dos 下的批处理相似,也就是用各类命令预先放入到一个文件中,方便一次性执行的一个程序文件,主要是方便管理员进行设置或者管理用的。但是它比 Windows 下的批处理更强大,比用其他编程程序编辑的程序效率更高,因为它使用了 Linux/Unix 下的命令。

    122 引用 • 73 回帖 • 1 关注
  • Hexo

    Hexo 是一款快速、简洁且高效的博客框架,使用 Node.js 编写。

    21 引用 • 140 回帖 • 14 关注
  • 运维

    互联网运维工作,以服务为中心,以稳定、安全、高效为三个基本点,确保公司的互联网业务能够 7×24 小时为用户提供高质量的服务。

    148 引用 • 257 回帖
  • 黑曜石

    黑曜石是一款强大的知识库工具,支持本地 Markdown 文件编辑,支持双向链接和关系图。

    A second brain, for you, forever.

    10 引用 • 88 回帖
  • App

    App(应用程序,Application 的缩写)一般指手机软件。

    90 引用 • 383 回帖
  • Kubernetes

    Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。

    109 引用 • 54 回帖 • 2 关注
  • Tomcat

    Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。

    162 引用 • 529 回帖 • 2 关注
  • Node.js

    Node.js 是一个基于 Chrome JavaScript 运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞 I/O 模型而得以轻量和高效。

    138 引用 • 268 回帖 • 147 关注
  • 周末

    星期六到星期天晚,实行五天工作制后,指每周的最后两天。再过几年可能就是三天了。

    14 引用 • 297 回帖