整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

用jsp:include指令动态加载文件

是Include.jsp文件

<!-- this is the Include.jsp -->

<%@ page contentType="text/html; charset=GB2312" %>

<%@ page language="java" %>

<HTML>

<HEAD>

<TITLE>动态加载文件</TITLE>

</HEAD>

<BODY>

<CENTER>

<FONT SIZE = 10 COLOR = blue>动态加载文件</FONT>

</CENTER>

<br>

<HR>

<br>

<font size=5>

<!-- 用jsp:include指令动态加载文件 -->

<jsp:include page="jspInsert.jsp">

<jsp:param name="name" value="jsp:include"/>

<jsp:param name="file" value="jspInsert.jsp"/>

</jsp:include>

</font>

</BODY>

</HTML>

下面是jspInsert.jsp文件

<HTML>

<BODY>

您好!这里我们用到了

<Font Color=Blue>

<%= request.getParameter("name")%>

</Font>

指令!

<BR>

我们在这里加载了文件:

<Font Color=Blue>

<%= request.getParameter("file")%>

</Font>。<br>

</HTML>

</BODY>

么是 CGI?

  • 公共网关接口(CGI),是一套标准,定义了信息是如何在 Web 服务器和客户端脚本之间进行交换的。

  • CGI 规范目前是由 NCSA 维护的,NCSA 定义 CGI 如下:

  • 公共网关接口(CGI),是一种用于外部网关程序与信息服务器(如 HTTP 服务器)对接的接口标准。

  • 目前的版本是 CGI/1.1,CGI/1.2 版本正在推进中。

Web 浏览

为了更好地了解 CGI 的概念,让我们点击一个超链接,浏览一个特定的网页或 URL,看看会发生什么。

  • 您的浏览器联系上 HTTP Web 服务器,并请求 URL,即文件名。

  • Web 服务器将解析 URL,并查找文件名。如果找到请求的文件,Web 服务器会把文件发送回浏览器,否则发送一条错误消息,表明您请求了一个错误的文件。

  • Web 浏览器从 Web 服务器获取响应,并根据接收到的响应来显示文件或错误消息。

然而,以这种方式搭建起来的 HTTP 服务器,不管何时请求目录中的某个文件,HTTP 服务器发送回来的不是该文件,而是以程序形式执行,并把执行产生的输出发送回浏览器显示出来。

公共网关接口(CGI),是使得应用程序(称为 CGI 程序或 CGI 脚本)能够与 Web 服务器以及客户端进行交互的标准协议。这些 CGI 程序可以用 Python、PERL、Shell、C 或 C++ 等进行编写。

CGI 架构图

下图演示了 CGI 的架构:

Web 服务器配置

在您进行 CGI 编程之前,请确保您的 Web 服务器支持 CGI,并已配置成可以处理 CGI 程序。所有由 HTTP 服务器执行的 CGI 程序,都必须在预配置的目录中。该目录称为 CGI 目录,按照惯例命名为 /var/www/cgi-bin。虽然 CGI 文件是 C++ 可执行文件,但是按照惯例它的扩展名是 .cgi

默认情况下,Apache Web 服务器会配置在 /var/www/cgi-bin 中运行 CGI 程序。如果您想指定其他目录来运行 CGI 脚本,您可以在 httpd.conf 文件中修改以下部分:

<Directory "/var/www/cgi-bin"> AllowOverride None Options ExecCGI Order allow,deny Allow from all</Directory><Directory "/var/www/cgi-bin">Options All</Directory>

在这里,我们假设已经配置好 Web 服务器并能成功运行,你可以运行任意的 CGI 程序,比如 Perl 或 Shell 等。

第一个 CGI 程序

请看下面的 C++ 程序:

实例

#include<iostream>usingnamespacestd; intmain(){cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>Hello World - 第一个 CGI 程序</title>\n"; cout << "</head>\n"; cout << "<body>\n"; cout << "<h2>Hello World! 这是我的第一个 CGI 程序</h2>\n"; cout << "</body>\n"; cout << "</html>\n"; return0;}

编译上面的代码,把可执行文件命名为 cplusplus.cgi,并把这个文件保存在 /var/www/cgi-bin 目录中。在运行 CGI 程序之前,请使用 chmod 755 cplusplus.cgi UNIX 命令来修改文件模式,确保文件可执行。访问可执行文件,您会看到下面的输出:

Hello World! 这是我的第一个 CGI 程序

上面的 C++ 程序是一个简单的程序,把它的输出写在 STDOUT 文件上,即显示在屏幕上。在这里,值得注意一点,第一行输出Content-type:text/html\r\n\r\n。这一行发送回浏览器,并指定要显示在浏览器窗口上的内容类型。您必须理解 CGI 的基本概念,这样才能进一步使用 Python 编写更多复杂的 CGI 程序。C++ CGI 程序可以与任何其他外部的系统(如 RDBMS)进行交互。

HTTP 头信息

Content-type:text/html\r\n\r\n 是 HTTP 头信息的组成部分,它被发送到浏览器,以便更好地理解页面内容。HTTP 头信息的形式如下:

HTTP 字段名称: 字段内容例如Content-type: text/html\r\n\r\n

还有一些其他的重要的 HTTP 头信息,这些在您的 CGI 编程中都会经常被用到。

头信息描述
Content-type:MIME 字符串,定义返回的文件格式。例如 Content-type:text/html。
Expires: Date信息变成无效的日期。浏览器使用它来判断一个页面何时需要刷新。一个有效的日期字符串的格式应为 01 Jan 1998 12:00:00 GMT。
Location: URL这个 URL 是指应该返回的 URL,而不是请求的 URL。你可以使用它来重定向一个请求到任意的文件。
Last-modified: Date资源的最后修改日期。
Content-length: N要返回的数据的长度,以字节为单位。浏览器使用这个值来表示一个文件的预计下载时间。
Set-Cookie: String通过 string 设置 cookie。

CGI 环境变量

所有的 CGI 程序都可以访问下列的环境变量。这些变量在编写 CGI 程序时扮演了非常重要的角色。

变量名描述
CONTENT_TYPE内容的数据类型。当客户端向服务器发送附加内容时使用。例如,文件上传等功能。
CONTENT_LENGTH查询的信息长度。只对 POST 请求可用。
HTTP_COOKIE以键 & 值对的形式返回设置的 cookies。
HTTP_USER_AGENT用户代理请求标头字段,递交用户发起请求的有关信息,包含了浏览器的名称、版本和其他平台性的附加信息。
PATH_INFOCGI 脚本的路径。
QUERY_STRING通过 GET 方法发送请求时的 URL 编码信息,包含 URL 中问号后面的参数。
REMOTE_ADDR发出请求的远程主机的 IP 地址。这在日志记录和认证时是非常有用的。
REMOTE_HOST发出请求的主机的完全限定名称。如果此信息不可用,则可以用 REMOTE_ADDR 来获取 IP 地址。
REQUEST_METHOD用于发出请求的方法。最常见的方法是 GET 和 POST。
SCRIPT_FILENAMECGI 脚本的完整路径。
SCRIPT_NAMECGI 脚本的名称。
SERVER_NAME服务器的主机名或 IP 地址。
SERVER_SOFTWARE服务器上运行的软件的名称和版本。

下面的 CGI 程序列出了所有的 CGI 变量。

实例

#include<iostream>#include<stdlib.h>usingnamespacestd; conststringENV[24] = {"COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE", "HTTP_ACCEPT", "HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "HTTP_CONNECTION", "HTTP_HOST", "HTTP_USER_AGENT", "PATH", "QUERY_STRING", "REMOTE_ADDR", "REMOTE_PORT", "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_FILENAME", "SCRIPT_NAME", "SERVER_ADDR", "SERVER_ADMIN", "SERVER_NAME","SERVER_PORT","SERVER_PROTOCOL", "SERVER_SIGNATURE","SERVER_SOFTWARE"}; intmain(){cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>CGI 环境变量</title>\n"; cout << "</head>\n"; cout << "<body>\n"; cout << "<table border = \"0\" cellspacing = \"2\">"; for(inti = 0; i < 24; i++ ){cout << "<tr><td>" << ENV[i] << "</td><td>"; // 尝试检索环境变量的值char *value = getenv(ENV[i].c_str()); if(value != 0){cout << value; }else{cout << "环境变量不存在。"; }cout << "</td></tr>\n"; }cout << "</table><\n"; cout << "</body>\n"; cout << "</html>\n"; return0;}

C++ CGI 库

在真实的实例中,您需要通过 CGI 程序执行许多操作。这里有一个专为 C++ 程序而编写的 CGI 库,我们可以从ftp://ftp.gnu.org/gnu/cgicc/ 上下载这个 CGI 库,并按照下面的步骤安装库:

$tar xzf cgicc-X.X.X.tar.gz$cd cgicc-X.X.X/ $./configure --prefix=/usr$make$make install

您可以点击 C++ CGI Lib Documentation,查看相关的库文档。

GET 和 POST 方法

您可能有遇到过这样的情况,当您需要从浏览器传递一些信息到 Web 服务器,最后再传到 CGI 程序。通常浏览器会使用两种方法把这个信息传到 Web 服务器,分别是 GET 和 POST 方法。

使用 GET 方法传递信息

GET 方法发送已编码的用户信息追加到页面请求中。页面和已编码信息通过 ? 字符分隔开,如下所示:

http://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2

GET 方法是默认的从浏览器向 Web 服务器传信息的方法,它会在浏览器的地址栏中生成一串很长的字符串。当您向服务器传密码或其他一些敏感信息时,不要使用 GET 方法。GET 方法有大小限制,在一个请求字符串中最多可以传 1024 个字符。

当使用 GET 方法时,是使用 QUERY_STRING http 头来传递信息,在 CGI 程序中可使用 QUERY_STRING 环境变量来访问。

您可以通过在 URL 后跟上简单连接的键值对,也可以通过使用 HTML <FORM> 标签的 GET 方法来传信息。

简单的 URL 实例:Get 方法

下面是一个简单的 URL,使用 GET 方法传递两个值给 hello_get.py 程序。

/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI

下面的实例生成 cpp_get.cgi CGI 程序,用于处理 Web 浏览器给出的输入。通过使用 C++ CGI 库,可以很容易地访问传递的信息:

实例

#include<iostream>#include<vector>#include<string>#include<stdio.h>#include<stdlib.h>#include<cgicc/CgiDefs.h>#include<cgicc/Cgicc.h>#include<cgicc/HTTPHTMLHeader.h>#include<cgicc/HTMLClasses.h>usingnamespacestd;usingnamespacecgicc; intmain(){CgiccformData; cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>使用 GET 和 POST 方法</title>\n"; cout << "</head>\n"; cout << "<body>\n"; form_iteratorfi = formData.getElement("first_name"); if( !fi->isEmpty() && fi != (*formData).end()){cout << "名:" << **fi << endl; }else{cout << "No text entered for first name" << endl; }cout << "<br/>\n"; fi = formData.getElement("last_name"); if( !fi->isEmpty() &&fi != (*formData).end()){cout << "姓:" << **fi << endl; }else{cout << "No text entered for last name" << endl; }cout << "<br/>\n"; cout << "</body>\n"; cout << "</html>\n"; return0;}

现在,编译上面的程序,如下所示:

$g++ -o cpp_get.cgi cpp_get.cpp -lcgicc

生成 cpp_get.cgi,并把它放在 CGI 目录中,并尝试使用下面的链接进行访问:

/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI

这会产生以下结果:

名:ZARA姓:ALI

简单的表单实例:GET 方法

下面是一个简单的实例,使用 HTML 表单和提交按钮传递两个值。我们将使用相同的 CGI 脚本 cpp_get.cgi 来处理输入。

<formaction="/cgi-bin/cpp_get.cgi"method="get">名:<inputtype="text"name="first_name"><br/> 姓:<inputtype="text"name="last_name"/><inputtype="submit"value="提交"/></form>

下面是上述表单的实际输出,请输入名和姓,然后点击提交按钮查看结果。

使用 POST 方法传递信息

一个更可靠的向 CGI 程序传递信息的方法是 POST 方法。这种方法打包信息的方式与 GET 方法相同,不同的是,它不是把信息以文本字符串形式放在 URL 中的 ? 之后进行传递,而是把它以单独的消息形式进行传递。该消息是以标准输入的形式传给 CGI 脚本的。

我们同样使用 cpp_get.cgi 程序来处理 POST 方法。让我们以同样的例子,通过使用 HTML 表单和提交按钮来传递两个值,只不过这次我们使用的不是 GET 方法,而是 POST 方法,如下所示:

<formaction="/cgi-bin/cpp_get.cgi"method="post">名:<inputtype="text"name="first_name"><br/>姓:<inputtype="text"name="last_name"/><inputtype="submit"value="提交"/></form>

向 CGI 程序传递复选框数据

当需要选择多个选项时,我们使用复选框。

下面的 HTML 代码实例是一个带有两个复选框的表单:

<formaction="/cgi-bin/cpp_checkbox.cgi"method="POST"target="_blank"><inputtype="checkbox"name="maths"value="on"/> 数学<inputtype="checkbox"name="physics"value="on"/> 物理<inputtype="submit"value="选择学科"/></form>

下面的 C++ 程序会生成 cpp_checkbox.cgi 脚本,用于处理 Web 浏览器通过复选框给出的输入。

实例

#include<iostream>#include<vector>#include<string>#include<stdio.h>#include<stdlib.h>#include<cgicc/CgiDefs.h>#include<cgicc/Cgicc.h>#include<cgicc/HTTPHTMLHeader.h>#include<cgicc/HTMLClasses.h>usingnamespacestd;usingnamespacecgicc; intmain(){CgiccformData; boolmaths_flag, physics_flag; cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>向 CGI 程序传递复选框数据</title>\n"; cout << "</head>\n"; cout << "<body>\n"; maths_flag = formData.queryCheckbox("maths"); if(maths_flag){cout << "Maths Flag: ON " << endl; }else{cout << "Maths Flag: OFF " << endl; }cout << "<br/>\n"; physics_flag = formData.queryCheckbox("physics"); if(physics_flag){cout << "Physics Flag: ON " << endl; }else{cout << "Physics Flag: OFF " << endl; }cout << "<br/>\n"; cout << "</body>\n"; cout << "</html>\n"; return0;}

向 CGI 程序传递单选按钮数据

当只需要选择一个选项时,我们使用单选按钮。

下面的 HTML 代码实例是一个带有两个单选按钮的表单:

<formaction="/cgi-bin/cpp_radiobutton.cgi"method="post"target="_blank"><inputtype="radio"name="subject"value="maths"checked="checked"/> 数学 <inputtype="radio"name="subject"value="physics"/> 物理<inputtype="submit"value="选择学科"/></form>

下面的 C++ 程序会生成 cpp_radiobutton.cgi 脚本,用于处理 Web 浏览器通过单选按钮给出的输入。

实例

#include<iostream>#include<vector>#include<string>#include<stdio.h>#include<stdlib.h>#include<cgicc/CgiDefs.h>#include<cgicc/Cgicc.h>#include<cgicc/HTTPHTMLHeader.h>#include<cgicc/HTMLClasses.h>usingnamespacestd;usingnamespacecgicc; intmain(){CgiccformData; cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>向 CGI 程序传递单选按钮数据</title>\n"; cout << "</head>\n"; cout << "<body>\n"; form_iteratorfi = formData.getElement("subject"); if( !fi->isEmpty() && fi != (*formData).end()){cout << "Radio box selected: " << **fi << endl; }cout << "<br/>\n"; cout << "</body>\n"; cout << "</html>\n"; return0;}

向 CGI 程序传递文本区域数据

当需要向 CGI 程序传递多行文本时,我们使用 TEXTAREA 元素。

下面的 HTML 代码实例是一个带有 TEXTAREA 框的表单:

<formaction="/cgi-bin/cpp_textarea.cgi"method="post"target="_blank"><textareaname="textcontent"cols="40"rows="4">请在这里输入文本...</textarea><inputtype="submit"value="提交"/></form>

下面的 C++ 程序会生成 cpp_textarea.cgi 脚本,用于处理 Web 浏览器通过文本区域给出的输入。

实例

#include<iostream>#include<vector>#include<string>#include<stdio.h>#include<stdlib.h>#include<cgicc/CgiDefs.h>#include<cgicc/Cgicc.h>#include<cgicc/HTTPHTMLHeader.h>#include<cgicc/HTMLClasses.h>usingnamespacestd;usingnamespacecgicc; intmain(){CgiccformData; cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>向 CGI 程序传递文本区域数据</title>\n"; cout << "</head>\n"; cout << "<body>\n"; form_iteratorfi = formData.getElement("textcontent"); if( !fi->isEmpty() && fi != (*formData).end()){cout << "Text Content: " << **fi << endl; }else{cout << "No text entered" << endl; }cout << "<br/>\n"; cout << "</body>\n"; cout << "</html>\n"; return0;}

向 CGI 程序传递下拉框数据

当有多个选项可用,但只能选择一个或两个选项时,我们使用下拉框。

下面的 HTML 代码实例是一个带有下拉框的表单:

<formaction="/cgi-bin/cpp_dropdown.cgi"method="post"target="_blank"><selectname="dropdown"><optionvalue="Maths"selected>数学</option><optionvalue="Physics">物理</option></select><inputtype="submit"value="提交"/></form>

下面的 C++ 程序会生成 cpp_dropdown.cgi 脚本,用于处理 Web 浏览器通过下拉框给出的输入。

实例

#include<iostream>#include<vector>#include<string>#include<stdio.h>#include<stdlib.h>#include<cgicc/CgiDefs.h>#include<cgicc/Cgicc.h>#include<cgicc/HTTPHTMLHeader.h>#include<cgicc/HTMLClasses.h>usingnamespacestd;usingnamespacecgicc; intmain(){CgiccformData; cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>向 CGI 程序传递下拉框数据</title>\n"; cout << "</head>\n"; cout << "<body>\n"; form_iteratorfi = formData.getElement("dropdown"); if( !fi->isEmpty() && fi != (*formData).end()){cout << "Value Selected: " << **fi << endl; }cout << "<br/>\n"; cout << "</body>\n"; cout << "</html>\n"; return0;}

在 CGI 中使用 Cookies

HTTP 协议是一种无状态的协议。但对于一个商业网站,它需要在不同页面间保持会话信息。例如,一个用户在完成多个页面的步骤之后结束注册。但是,如何在所有网页中保持用户的会话信息。

在许多情况下,使用 cookies 是记忆和跟踪有关用户喜好、购买、佣金以及其他为追求更好的游客体验或网站统计所需信息的最有效的方法。

它是如何工作的

服务器以 cookie 的形式向访客的浏览器发送一些数据。如果浏览器接受了 cookie,则 cookie 会以纯文本记录的形式存储在访客的硬盘上。现在,当访客访问网站上的另一个页面时,会检索 cookie。一旦找到 cookie,服务器就知道存储了什么。

cookie 是一种纯文本的数据记录,带有 5 个可变长度的字段:

  • Expires : cookie 的过期日期。如果此字段留空,cookie 会在访客退出浏览器时过期。

  • Domain : 网站的域名。

  • Path : 设置 cookie 的目录或网页的路径。如果您想从任意的目录或网页检索 cookie,此字段可以留空。

  • Secure : 如果此字段包含单词 "secure",那么 cookie 只能通过安全服务器进行检索。如果此字段留空,则不存在该限制。

  • Name=Value : cookie 以键值对的形式被设置和获取。

设置 Cookies

向浏览器发送 cookies 是非常简单的。这些 cookies 会在 Content-type 字段之前,与 HTTP 头一起被发送。假设您想设置 UserID 和 Password 为 cookies,设置 cookies 的步骤如下所示:

实例

#include<iostream>usingnamespacestd; intmain(){cout << "Set-Cookie:UserID=XYZ;\r\n"; cout << "Set-Cookie:Password=XYZ123;\r\n"; cout << "Set-Cookie:Domain=www.w3cschool.cc;\r\n"; cout << "Set-Cookie:Path=/perl;\n"; cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>CGI 中的 Cookies</title>\n"; cout << "</head>\n"; cout << "<body>\n"; cout << "设置 cookies" << endl; cout << "<br/>\n"; cout << "</body>\n"; cout << "</html>\n"; return0;}

从这个实例中,我们了解了如何设置 cookies。我们使用 Set-Cookie HTTP 头来设置 cookies。

在这里,有一些设置 cookies 的属性是可选的,比如 Expires、Domain 和 Path。值得注意的是,cookies 是在发送行"Content-type:text/html\r\n\r\n 之前被设置的。

编译上面的程序,生成 setcookies.cgi,并尝试使用下面的链接设置 cookies。它会在您的计算机上设置四个 cookies:

/cgi-bin/setcookies.cgi

获取 Cookies

检索所有设置的 cookies 是非常简单的。cookies 被存储在 CGI 环境变量 HTTP_COOKIE 中,且它们的形式如下:

key1=value1;key2=value2;key3=value3....

下面的实例演示了如何获取 cookies。

实例

#include<iostream>#include<vector>#include<string>#include<stdio.h>#include<stdlib.h>#include<cgicc/CgiDefs.h>#include<cgicc/Cgicc.h>#include<cgicc/HTTPHTMLHeader.h>#include<cgicc/HTMLClasses.h>usingnamespacestd;usingnamespacecgicc; intmain(){Cgicccgi; const_cookie_iteratorcci; cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>CGI 中的 Cookies</title>\n"; cout << "</head>\n"; cout << "<body>\n"; cout << "<table border = \"0\" cellspacing = \"2\">"; // 获取环境变量constCgiEnvironment& env = cgi.getEnvironment(); for(cci = env.getCookieList().begin(); cci != env.getCookieList().end(); ++cci){cout << "<tr><td>" << cci->getName() << "</td><td>"; cout << cci->getValue(); cout << "</td></tr>\n"; }cout << "</table><\n"; cout << "<br/>\n"; cout << "</body>\n"; cout << "</html>\n"; return0;}

现在,编译上面的程序,生成 getcookies.cgi,并尝试使用下面的链接获取您的计算机上所有可用的 cookies:

/cgi-bin/getcookies.cgi

这会产生一个列表,显示了上一节中设置的四个 cookies 以及您的计算机上所有其他的 cookies:

UserID XYZPassword XYZ123Domain www.w3cschool.ccPath /perl

文件上传实例

为了上传一个文件,HTML 表单必须把 enctype 属性设置为 multipart/form-data。带有文件类型的 input 标签会创建一个 "Browse" 按钮。

<html><body><formenctype="multipart/form-data"action="/cgi-bin/cpp_uploadfile.cgi"method="post"><p>文件:<inputtype="file"name="userfile"/></p><p><inputtype="submit"value="上传"/></p></form></body></html>

这段代码的结果是下面的表单:

文件:

注意:上面的实例已经故意禁用了保存上传的文件在我们的服务器上。您可以在自己的服务器上尝试上面的代码。

下面是用于处理文件上传的脚本 cpp_uploadfile.cpp

实例

#include<iostream>#include<vector>#include<string>#include<stdio.h>#include<stdlib.h>#include<cgicc/CgiDefs.h>#include<cgicc/Cgicc.h>#include<cgicc/HTTPHTMLHeader.h>#include<cgicc/HTMLClasses.h>usingnamespacestd;usingnamespacecgicc; intmain(){Cgicccgi; cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>CGI 中的文件上传</title>\n"; cout << "</head>\n"; cout << "<body>\n"; // 获取要被上传的文件列表const_file_iteratorfile = cgi.getFile("userfile"); if(file != cgi.getFiles().end()){// 在 cout 中发送数据类型cout << HTTPContentHeader(file->getDataType()); // 在 cout 中写入内容file->writeToStream(cout); }cout << "<文件上传成功>\n"; cout << "</body>\n"; cout << "</html>\n"; return0;}

上面的实例是在 cout 流中写入内容,但您可以打开文件流,并把上传的文件内容保存在目标位置的某个文件中。

译自: https://opensource.com/article/18/10/book-to-website-epub-using-pandoc

作者: Kiko Fernandez-reyes

译者: jlztan

通过 Markdown 和 Pandoc,可以做到编写一次,发布两次。

Pandoc 是一个命令行工具,用于将文件从一种标记语言转换为另一种标记语言。在我 对 Pandoc 的简介 一文中,我演示了如何把 Markdown 编写的文本转换为网页、幻灯片和 PDF。

在这篇后续文章中,我将深入探讨 Pandoc ,展示如何从同一个 Markdown 源文件生成网页和 ePub 格式的电子书。我将使用我即将发布的电子书《 面向对象思想的 GRASP 原则 》为例进行讲解,这本电子书正是通过以下过程创建的。

首先,我将解释这本书使用的文件结构,然后介绍如何使用 Pandoc 生成网页并将其部署在 GitHub 上;最后,我演示了如何生成对应的 ePub 格式电子书。

你可以在我的 GitHub 仓库 Programming Fight Club 中找到相应代码。

设置图书结构

我用 Markdown 语法完成了所有的写作,你也可以使用 HTML 标记,但是当 Pandoc 将 Markdown 转换为 ePub 文档时,引入的 HTML 标记越多,出现问题的风险就越高。我的书按照每章一个文件的形式进行组织,用 Markdown 的 H1 标记(#)声明每章的标题。你也可以在每个文件中放置多个章节,但将它们放在单独的文件中可以更轻松地查找内容并在以后进行更新。

元信息遵循类似的模式,每种输出格式都有自己的元信息文件。元信息文件定义有关文档的信息,例如要添加到 HTML 中的文本或 ePub 的许可证。我将所有 Markdown 文档存储在名为 parts 的文件夹中(这对于用来生成网页和 ePub 的 Makefile 非常重要)。下面以一个例子进行说明,让我们看一下目录,前言和关于本书(分为 toc.md、preface.md 和 about.md 三个文件)这三部分,为清楚起见,我们将省略其余的章节。

关于本书这部分内容的开头部分类似:

# About this book {-}

## Who should read this book {-}

Before creating a complex software system one needs to create a solid foundation.

General Responsibility Assignment Software Principles (GRASP) are guidelines to assign

responsibilities to software classes in object-oriented programming.

每一章完成后,下一步就是添加元信息来设置网页和 ePub 的格式。

生成网页

创建 HTML 元信息文件

我创建的网页的元信息文件(web-metadata.yaml)是一个简单的 YAML 文件,其中包含 <head> 标签中的作者、标题、和版权等信息,以及 HTML 文件中开头和结尾的内容。

我建议(至少)包括 web-metadata.yaml 文件中的以下字段:

---

title: <a href="/grasp-principles/toc/">GRASP principles for the Object-oriented mind</a>

author: Kiko Fernandez-Reyes

rights: 2017 Kiko Fernandez-Reyes, CC-BY-NC-SA 4.0 International

header-includes:

- |

```{=html}

<link href="https://fonts.googleapis.com/css?family=Inconsolata" rel="stylesheet">

<link href="https://fonts.googleapis.com/css?family=Gentium+Basic|Inconsolata" rel="stylesheet">

```

include-before:

- |

```{=html}

<p>If you like this book, please consider

spreading the word or

<a href="https://www.buymeacoffee.com/programming">

buying me a coffee

</a>

</p>

```

include-after:

- |

```{=html}

<div class="footnotes">

<hr>

<div class="container">

<nav class="pagination" role="pagination">

<ul>

<p>

<span class="page-number">Designed with</span> ❤️ <span class="page-number"> from Uppsala, Sweden</span>

</p>

<p>

<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="http://www.hmttv.cn/uploadfile/2024/0807/20240807030249413.png" /></a>

</p>

</ul>

</nav>

</div>

</div>

```

---

下面几个变量需要注意一下:

  • header-includes 变量包含将要嵌入 <head> 标签的 HTML 文本。
  • 调用变量后的下一行必须是 - |。再往下一行必须以与 | 对齐的三个反引号开始,否则 Pandoc 将无法识别。{= html} 告诉 Pandoc 其中的内容是原始文本,不应该作为 Markdown 处理。(为此,需要检查 Pandoc 中的 raw_attribute 扩展是否已启用。要进行此检查,键入 pandoc --list-extensions | grep raw 并确保返回的列表包含名为 + raw_html 的项目,加号表示已启用。)
  • 变量 include-before 在网页开头添加一些 HTML 文本,此处我请求读者帮忙宣传我的书或给我打赏。
  • include-after 变量在网页末尾添加原始 HTML 文本,同时显示我的图书许可证。

这些只是其中一部分可用的变量,查看 HTML 中的模板变量(我的文章 Pandoc简介 中介绍了如何查看 LaTeX 的模版变量,查看 HTML 模版变量的过程是相同的)对其余变量进行了解。

将网页分成多章

网页可以作为一个整体生成,这会产生一个包含所有内容的长页面;也可以分成多章,我认为这样会更容易阅读。我将解释如何将网页划分为多章,以便读者不会被长网页吓到。

为了使网页易于在 GitHub Pages 上部署,需要创建一个名为 docs 的根文件夹(这是 GitHub Pages 默认用于渲染网页的根文件夹)。然后我们需要为 docs 下的每一章创建文件夹,将 HTML 内容放在各自的文件夹中,将文件内容放在名为 index.html 的文件中。

例如,about.md 文件将转换成名为 index.html 的文件,该文件位于名为 about(about/index.html)的文件夹中。这样,当用户键入 http://<your-website.com>/about/ 时,文件夹中的 index.html 文件将显示在其浏览器中。

下面的 Makefile 将执行上述所有操作:

# Your book files

DEPENDENCIES= toc preface about

# Placement of your HTML files

DOCS=docs

all: web

web: setup $(DEPENDENCIES)

@cp $(DOCS)/toc/index.html $(DOCS)

# Creation and copy of stylesheet and images into

# the assets folder. This is important to deploy the

# website to Github Pages.

setup:

@mkdir -p $(DOCS)

@cp -r assets $(DOCS)

# Creation of folder and index.html file on a

# per-chapter basis

$(DEPENDENCIES):

@mkdir -p $(DOCS)/$@

@pandoc -s --toc web-metadata.yaml parts/$@.md \

-c /assets/pandoc.css -o $(DOCS)/$@/index.html

clean:

@rm -rf $(DOCS)

.PHONY: all clean web setup

选项 - c /assets/pandoc.css 声明要使用的 CSS 样式表,它将从 /assets/pandoc.cs 中获取。也就是说,在 <head> 标签内,Pandoc 会添加这样一行:

<link rel="stylesheet" href="/assets/pandoc.css">

使用下面的命令生成网页:

make

根文件夹现在应该包含如下所示的文件结构:

.---parts

| |--- toc.md

| |--- preface.md

| |--- about.md

|

|---docs

|--- assets/

|--- index.html

|--- toc

| |--- index.html

|

|--- preface

| |--- index.html

|

|--- about

|--- index.html

部署网页

通过以下步骤将网页部署到 GitHub 上:

  1. 创建一个新的 GitHub 仓库
  2. 将内容推送到新创建的仓库
  3. 找到仓库设置中的 GitHub Pages 部分,选择 Source 选项让 GitHub 使用主分支的内容

你可以在 GitHub Pages 的网站上获得更多详细信息。

我的书的网页 便是通过上述过程生成的,可以在网页上查看结果。

生成电子书

创建 ePub 格式的元信息文件

ePub 格式的元信息文件 epub-meta.yaml 和 HTML 元信息文件是类似的。主要区别在于 ePub 提供了其他模板变量,例如 publisher 和 cover-image 。ePub 格式图书的样式表可能与网页所用的不同,在这里我使用一个名为 epub.css 的样式表。

---

title: 'GRASP principles for the Object-oriented Mind'

publisher: 'Programming Language Fight Club'

author: Kiko Fernandez-Reyes

rights: 2017 Kiko Fernandez-Reyes, CC-BY-NC-SA 4.0 International

cover-image: assets/cover.png

stylesheet: assets/epub.css

...

将以下内容添加到之前的 Makefile 中:

epub:

@pandoc -s --toc epub-meta.yaml \

$(addprefix parts/, $(DEPENDENCIES:=.md)) -o $(DOCS)/assets/book.epub

用于产生 ePub 格式图书的命令从 HTML 版本获取所有依赖项(每章的名称),向它们添加 Markdown 扩展,并在它们前面加上每一章的文件夹路径,以便让 Pandoc 知道如何进行处理。例如,如果 $(DEPENDENCIES 变量只包含 “前言” 和 “关于本书” 两章,那么 Makefile 将会这样调用:

@pandoc -s --toc epub-meta.yaml \

parts/preface.md parts/about.md -o $(DOCS)/assets/book.epub

Pandoc 将提取这两章的内容,然后进行组合,最后生成 ePub 格式的电子书,并放在 Assets 文件夹中。

这是使用此过程创建 ePub 格式电子书的一个 示例 。

过程总结

从 Markdown 文件创建网页和 ePub 格式电子书的过程并不困难,但有很多细节需要注意。遵循以下大纲可能使你更容易使用 Pandoc。

  • HTML 图书:
  • 使用 Markdown 语法创建每章内容
  • 添加元信息
  • 创建一个 Makefile 将各个部分组合在一起
  • 设置 GitHub Pages
  • 部署
  • ePub 电子书:
  • 使用之前创建的每一章内容
  • 添加新的元信息文件
  • 创建一个 Makefile 以将各个部分组合在一起
  • 设置 GitHub Pages
  • 部署

via: https://opensource.com/article/18/10/book-to-website-epub-using-pandoc

作者: Kiko Fernandez-Reyes 选题: lujun9972 译者: jlztan 校对: wxy

本文由 LCTT 原创编译, Linux中国 荣誉推出

点击“了解更多”可访问文内链接