搭建微信 AI 机器人

公众号申请

任务时间:10min ~ 15min

注册一个公众号

首先进入微信公众号平台,然后注册一个账号,类型选择订阅号,然后根据相应的提示完成信息的填写和验证。具体操作可参考以下视频。

  • 视频 – 注册公众号

后台服务器的搭建

任务时间:10min ~ 15min

安装 NodeJS

首先执行以下命令

sudo su

下载最新的稳定版 v6.10.3 到本地

wget https://nodejs.org/dist/v6.10.3/node-v6.10.3-linux-x64.tar.xz

下载完成后, 将其解压

tar xvJf node-v6.10.3-linux-x64.tar.xz

将解压的 Node.js 目录移动到 /usr/local 目录下

mv node-v6.10.3-linux-x64 /usr/local/node-v6

配置 node 软链接到 /bin 目录

ln -s /usr/local/node-v6/bin/node /bin/node

配置 NPM

NPM 是 Node.js 的包管理和分发工具。它可以让 Node.js 开发者能够更加轻松的共享代码和共用代码片段

下载 node 的压缩包中已经包含了 npm , 我们只需要将其软链接到 bin 目录下即可

ln -s /usr/local/node-v6/bin/npm /bin/npm

配置环境变量

将 /usr/local/node-v6/bin 目录添加到 $PATH 环境变量中可以方便地使用通过 npm 全局安装的第三方工具

echo 'export PATH=/usr/local/node-v6/bin:$PATH' >> /etc/profile

生效环境变量

source /etc/profile

安装 Express

安装 Express 应用脚手架:

npm install express-generator -g

创建新的项目

执行

cd /data/
express yourApp

这时候会在 /data 生成应用目录 /yourApp,继续执行以下命令

cd yourApp
npm install
npm start

此时服务已经在 http://<您的 CVM IP 地址>:3000 上启动,可以在浏览器中浏览测试。

进入开发者模式

任务时间:10min ~ 15min

配置服务器信息,进入开发者模式

微信平台会去验证你填写的服务器的有效性,所以配置服务器前要先编写服务器有效性验证的代码。

在终端使用 Ctrl + C 终止刚才启动的服务,然后执行以下命令安装依赖的文件:

npm install xml2js sha1 querystring
  • 编辑 app.js,参考下面的代码:
示例代码:/data/yourApp/app.js
var express = require('express');
var app = express();
var http = require('http');
var sha1 = require('sha1');
var xml2js = require('xml2js');
var querystring  = require('querystring');

process.env.PORT = '80';
function checkSignature(req) {
  // 获取校验参数
  var signature = req.query.signature;
  var timestamp = req.query.timestamp;
  var nonce = req.query.nonce;

  // 此处为实验分配了一个 token,也可以修改为自己的 token
  var token = 'Password';

  // 按照字典排序
  var params = [token, timestamp, nonce];
  params.sort();

  // 连接
  var str = sha1(params.join(""));

  // 返回签名是否一致
  return str == signature;
}

// 接入验证
app.get('/', function(req, res) {
  // 签名成功
  if (checkSignature(req)) {
    res.send(200, req.query.echostr);
  } else {
    res.send(200, 'fail');
  }
});

module.exports = app;

启动服务:

npm start

登录微信公众号平台,然后拉到页面的最下面,点击基本配置菜单。填写以下信息:

  • 服务器地址(URL):填写 <您的 CVM IP 地址> [?]
  • 令牌(Token):填写上面代码中使用到的 Token,如无修改,直接使用 Password
  • 消息加解密密钥(EncodingAESKey):随机字符串。可以让平台生成。
  • 消息加解密方式:选择明文方式

配置完毕后点击提交,提交成功后,在基本配置菜单页面点击启用

此时,微信用户给公众号发送的信息,会由微信平台转发给您的服务器,然后您的服务器进行处理后返回给用户。

在页面点击提交,检查是否可以提交成功。

这是您的服务器地址,服务器主要是用于实现机器人的逻辑。主机部分必须是公网 IP 或者可解析到公网 IP 的域名。

机器人的实现

任务时间:10min ~ 15min

实现机器人功能

首先在图灵机器人官网注册和申请一个机器人,具体步骤可以参考视频。

  • 视频 – 申请图灵机器人

申请之后,我们来调用 API 实现机器人的功能。

编辑 app.js,参考下面的代码 [?]

示例代码:/data/yourApp/app.js
var express = require('express');
var app = express();
var http = require('http');
var sha1 = require('sha1');
var xml2js = require('xml2js');
var querystring  = require('querystring');

process.env.PORT = '80';
function checkSignature(req) {
  // 获取校验参数
  var signature = req.query.signature;
  var timestamp = req.query.timestamp;
  var nonce = req.query.nonce;

  // 此处为实验分配了一个 token,也可以修改为自己的 token
  var token = 'Password';

  // 按照字典排序
  var params = [token, timestamp, nonce];
  params.sort();

  // 连接
  var str = sha1(params.join(""));

  // 返回签名是否一致
  return str == signature;
}

// 接入验证
app.get('/', function(req, res) {
  // 签名成功
  if (checkSignature(req)) {
    res.send(200, req.query.echostr);
  } else {
    res.send(200, 'fail');
  }
});

function handler(req, res) {
  let  buf = '';
  // 获取XML内容
  req.setEncoding('utf8');
  req.on('data', function(chunk) {
    buf += chunk;
  });
  // 内容接收完毕
  req.on('end', function() {
    xml2js.parseString(buf, function(err, json) {
      if (err) {
        err.status = 400;
      } else {
        req.body = json;
      }
    });

    let data = req.body.xml;
    var msg = {
      "toUserName" : data.FromUserName[0],
      "fromUserName" : data.ToUserName[0],
      "createTime" : data.CreateTime[0],
      "msgType" : data.MsgType[0],
      "content" : data.Content[0],
      "msgId" : data.MsgId[0]
    };
    request(msg, req, res)

  });
}

function request(data, req, res) { 
  var msg = {             
    "key":'6d76234cf7ee488d84aa1a54397ae866',   // 可以填入自己申请的机器人的apiKey            
    "info": data.content,             
    "userid": ~~(Math.random() * 99999)
  };  
  var text = querystring.stringify(msg);    
  var options = {  
      hostname: 'www.tuling123.com',  
      path: '/openapi/api',  
      method: 'POST',
      headers: {  
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'  
    }  
  };  

  var requestObj = http.request(options, function (response) {  
      var result = '';
      response.setEncoding('utf8');  
      response.on('data', function (chunk) {  
         result +=  chunk;
      });  
      response.on('end',function() {  
      try{
          var obj = JSON.parse(result);
        }
          catch(e){
              data.content = e.message;
          echo(data, res);
          return;
            }
        data.content = obj.text;
        echo(data, res);
      })
  });  

  requestObj.on('error', function (e) {
      console.log('problem with request: ' + e.message);  
      data.content = e.message;  
      echo(data, res);
  });  
  requestObj.write(text);
  requestObj.end();  
}
function echo(data, res) {
  var time = Math.round(new Date().getTime() / 1000);
  var output = "" +
              "<xml>" +
                 "<ToUserName><![CDATA[" + data.toUserName + "]]></ToUserName>" +
                 "<FromUserName><![CDATA[" + data.fromUserName + "]]></FromUserName>" +
                 "<CreateTime>" + time + "</CreateTime>" +
                 "<MsgType><![CDATA[" + data.msgType + "]]></MsgType>" +
                 "<Content><![CDATA[" + data.content + "]]></Content>" +
              "</xml>";

  res.type('xml');
  res.send(output);
}
// Start
app.post('/', function(req, res) {
  handler(req, res);
});

module.exports = app;

操作键盘按下 Ctrl + C 终止之前的服务,然后重新启动:

npm start

您可用对比模式查看新增的部分

部署完成

任务时间:3min ~ 5min

访问服务

在微信公众号中关注自己的公众号,然后发送信息给公众号,测试是否有返回。在微信公众号平台可以对公众号信息进行管理。更复杂的逻辑可以参考微信公众号平台开发者文档图灵机器人开发文档

大功告成

恭喜您已经完成了搭建微信 AI 机器人的学习

搭建 Docker 环境

安装 Docker

Docker 软件包已经包括在默认的 CentOS-Extras 软件源里。因此想要安装 docker,只需要运行下面的 yum 命令:

yum install docker-io -y

直接yum安装,安装成功后查看版本

docker -v

启动docker

service docker start

设置开机启动

chkconfig docker on

配置 Docker

因为国内访问 Docker Hub 较慢, 可以使用腾讯云提供的国内镜像源, 加速访问 Docker Hub

依次执行以下命令

echo "OPTIONS='--registry-mirror=https://mirror.ccs.tencentyun.com'" >> /etc/sysconfig/docker
systemctl daemon-reload
service docker restart

Docker 的简单操作

任务时间:10min ~ 20min

下载镜像

下载一个官方的 CentOS 镜像到本地

docker pull centos

下载好的镜像就会出现在镜像列表里

docker images

运行容器

这时我们可以在刚才下载的 CentOS 镜像生成的容器内操作了。

生成一个 centos 镜像为模板的容器并使用 bash shell

docker run -it centos /bin/bash

这个时候可以看到命令行的前端已经变成了 [root@(一串 hash Id)] 的形式, 这说明我们已经成功进入了 CentOS 容器。

在容器内执行任意命令, 不会影响到宿主机, 如下

mkdir -p /data/simple_docker

可以看到 /data 目录下已经创建成功了 simple_docker 文件夹

ls /data

退出容器

exit

查看宿主机的 /data 目录, 并没有 simple_docker 文件夹, 说明容器内的操作不会影响到宿主机

ls /data

保存容器

查看所有的容器信息, 能获取容器的id

docker ps -a

然后执行如下命令[?],保存镜像:

docker commit -m="备注" 你的CONTAINER_ID 你的IMAGE

请自行将 -m 后面的信息改成自己的容器的信息

大功告成!

恭喜你结束了 Docker 的教程并学会了 Docker 的一些基本操作,

 

使用docker attach命令
我们使用
docker attach db3 或者 docker attach d48b21a7e439

 

db3是后台容器的NAMES,d48b21a7e439是容器的进程ID  CONTAINER ID
然后就进去了这个容器的ssh界面。
但是它有一个缺点,只要这个连接终止,或者使用了exit命令,容器就会退出后台运行
 
 
使用docker exec命令
这个命令使用exit命令后,不会退出后台,一般使用这个命令,使用方法如下
docker exec -it db3 /bin/sh 或者 docker exec -it d48b21a7e439 /bin/sh

搭建微信订阅号后台服务

搭建微信订阅号后台服务

准备域名

任务时间:20min ~ 40min

微信公众平台需要配置服务器地址 URL 访问,在实验开始之前,我们要准备域名。

域名注册

如果您还没有域名,可以在腾讯云上选购,过程可以参考下面的视频。

  • 视频 – 在腾讯云上购买域名

域名解析

域名购买完成后, 需要将域名解析到实验云主机上,实验云主机的 IP 为:

<您的 CVM IP 地址>

在腾讯云购买的域名,可以到控制台添加解析记录,过程可参考下面的视频:

  • 视频 – 如何在腾讯云上解析域名

域名设置解析后需要过一段时间才会生效,通过 ping 命令检查域名是否生效 [?],如:

ping www.yourmpdomain.com

如果 ping 命令返回的信息中含有你设置的解析的 IP 地址,说明解析成功。

注意替换下面命令中的 www.yourmpdomain.com 为您自己的注册的域名

申请微信个人订阅号

任务时间:5min ~ 10min

在开始搭建我们的订阅号服务器之前,需要先拿到订阅号相关信息。

注册开发者账号

如果你还不是微信订阅号开发者,请先在微信公众平台注册:

https://mp.weixin.qq.com

具体注册流程可参考如下视频:

  • 视频 – 注册开发者账号

若您已注册,请点击下一步。

获取微信订阅号公众平台认证字段信息

我们需要获取3个字段:AppID Token EncodingAESKey。

登录微信公众平台,依次进入 开发 – 基本配置 可以拿到 AppID。

基本配置 – 服务器配置 – 修改配置 表单中:

URL 填第一步申请的域名;

Token 用户根据提示填写,用于后面校验服务端合法性;

EncodingAESKey 点击随机生成按钮来生成。

当点击表单提交按钮时,微信会通过 Token 来校验 URL 的合法性,这个我们在后面步骤实现,此界面暂时保留不关闭。

AppID Token EncodingAESKey 这3个参数具体的获取步骤也可以参照下面的视频

  • 视频 – 获取微信订阅号信息

搭建 HTTP 服务

任务时间:15min ~ 30min

下面的步骤,将带大家在服务器上使用 Node 和 Express 搭建一个 HTTP 服务器

安装 NodeJS 和 NPM

使用下面的命令安装 NodeJS 和 NPM

curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash -
yum install nodejs -y

安装完成后,使用下面的命令测试安装结果

node -v

编写 HTTP Server 源码

创建工作目录

使用下面的命令在服务器创建一个工作目录:

mkdir -p /data/release/weapp

进入此工作目录

cd /data/release/weapp

创建 package.json

在刚才创建的工作目录创建 package.json,添加我们服务器包的名称和版本号,可参考下面的示例。

示例代码:/data/release/weapp/package.json
{
    "name": "weapp",
    "version": "1.0.0"
}

完成后,使用 Ctrl + S 保存文件

添加 Server 源码

在工作目录创建 app.js,使用 Express.js 来监听 5050 端口[?],可参考下面的示例代码(注:请将 app.js 文件中的token/appid/encodingAESKey等配置项替换为您的订阅号对应的取值)。

示例代码:/data/release/weapp/app.js
// 引用 express 来支持 HTTP Server 的实现
const express = require('express');

// 引用微信公共平台自动回复消息接口服务中间件
var wechat = require('wechat');

// 创建一个 express 实例
const app = express();

// 配置微信公众平台参数,在教程第二步中获取
var config = {
    token: 'your token', // 填第二步中获取的 `token`
    appid: 'your appid', // 填第二步中获取的 `appid`
    encodingAESKey: 'your encodingAESKey', // 填第二步中获取的 `encodingAESKey`
    checkSignature: true // 可选,默认为true。由于微信公众平台接口调试工具在明文模式下不发送签名,所以如要使用该测试工具,请将其设置为false 
};

app.use(express.query());

app.use('/', wechat(config, function (req, res, next) {
    res.reply({
        content: '你好,Hello World!',
        type: 'text'
    });
}));

// 监听端口,等待连接
const port = 5050;
app.listen(port);

// 输出服务器启动日志
console.log(`Server listening at http://127.0.0.1:${port}`);

本实验会以 5050 端口的打开作为实验步骤完成的依据,为了后面的实验步骤顺利进行,请不要使用其它端口号

运行 HTTP 服务

安装 PM2

在开始之前,我们先来安装 [PM2]

npm install pm2 --global

PM2 安装时间可能稍长,请耐心等候 [?]

安装 Express

我们的服务器源码里使用到了 Express 模块,下面的命令使用 NPM 来安装 Express

cd /data/release/weapp
npm install express --save

安装 Wechat

我们的服务器源码里使用到了 Wechat 模块,下面的命令使用 NPM 来安装 Wechat

cd /data/release/weapp
npm install wechat --save

启动服务

安装完成后,使用 PM2 来启动 HTTP 服务

cd /data/release/weapp
pm2 start app.js

现在,您的 HTTP 服务已经在 http://<您的 CVM IP 地址>:5050 运行

要查看服务输出的日志,可以使用下面的命令:

pm2 logs

如果要重启服务,可以使用下面的命令:

pm2 restart app

我们使用 PM2 来进行 Node 进程的运行、监控和管理

NPM 仓库在国内访问速度可能不太理想,如果实在太慢可以尝试使用 CNPM 的 Registry 进行安装:npm install pm2 -g --registry=https://r.cnpmjs.org/

搭建 nginx 对外服务

任务时间:15min ~ 30min

NodeJs只是侦听的机器上的 5050 端口,我们使用 nginx 侦听 80 端口提供对外域名服务

安装 Nginx

在 CentOS 上,可直接使用 yum 来安装 Nginx

yum install nginx -y

安装完成后,使用 nginx 命令启动 Nginx:

nginx

此时,访问 http://<您的域名> 可以看到 Nginx 的测试页面 [?]

如果无法访问,请重试用 nginx -s reload 命令重启 Nginx

配置 HTTP 反向代理

外网用户访问服务器的 Web 服务由 Nginx 提供,Nginx 需要配置反向代理才能使得 Web 服务转发到本地的 Node 服务。

Nginx 配置目录在 /etc/nginx/conf.d,我们在该目录创建 wechat.conf

示例代码:/etc/nginx/conf.d/wechat.conf
server {
        listen 80;
        server_name www.example.com; # 改为第一步申请的域名

        location / {
            proxy_pass http://127.0.0.1:5050;
        }
    }

按 Ctrl + S 保存配置文件,让 Nginx 重新加载配置使其生效:

nginx -s reload

在浏览器通过 http 的方式访问你解析的域名来测试 HTTP 是否成功启动

使用Server端回复微信消息

任务时间:1min ~ 5min

提交服务端配置

我们将第二步微信公众平台中保留的表单提交,同时将 基本配置 – 服务器配置 启用

关注、发送与消息回复

首先通过二维码关注微信订阅号

在聊天界面向微信公众号发送一条消息

最终我们会回到一条 你好,Hello World! 的回复

大功搞成

恭喜!您已经完成了搭建微信订阅号后台服务的实验内容!您可以留用或者购买 Linux 版本的 CVM 继续学习。

Express 入门

Express 入门

安装 NodeJS

任务时间:5min ~ 10min

安装 NodeJS

在终端中,使用下面的命令安装 NodeJS:

curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash -
yum -y install nodejs

安装完成后,可使用下面的命令测试安装结果:

node -v

安装 Express

任务时间:5min ~ 10min

创建工作目录

使用下面的命令在服务器创建一个工作目录:

mkdir -p /data/release/hello

进入此工作目录:

cd /data/release/hello

初始化项目

通过 npm init 命令为你的应用创建一个 package.json 文件。欲了解 package.json 是如何起作用的,请参考 Specifics of npm’s package.json handling

npm init

此命令将要求你输入几个参数,例如此应用的名称和版本。 除 entry point: (index.js) 参数外,其他参数你可以直接按 “回车” 键接受默认设置即可。

对于 entry point: (index.js) 参数,键入 app.js 或者你所希望的名称,这是当前应用的入口文件;如果你希望采用默认的 index.js 文件名,只需按 “回车” 键即可。

安装 Express

接下来安装 Express 并将其保存到依赖列表中:

npm install express --save

如果只是临时安装 Express,不想将它添加到依赖列表中,只需略去 --save 参数即可 [?]

npm install express

安装 Node 模块时,如果指定了 --save 参数,那么此模块将被添加到 package.json 文件中 dependencies 依赖列表中。 然后通过 npm install 命令即可自动安装依赖列表中所列出的所有模块。

Hello world

任务时间:5min ~ 10min

创建 app.js

在 hello 目录中,创建 app.js,然后将下列代码复制进去:

示例代码:/data/release/hello/app.js
var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

var server = app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

完成后,使用 Ctrl + S 保存文件。

[?] 上面的代码启动一个服务并监听从 3000 端口进入的所有连接请求。他将对所有 (/) URL 或 路由 返回 “Hello World!” 字符串。对于其他所有路径全部返回 404 Not Found 。

req (请求) 和 res (响应) 与 Node 提供的对象完全一致,因此,你可以调用 req.pipe()、req.on(‘data’, callback) 以及任何 Node 提供的方法。

启动应用

通过如下命令启动此应用:

node app.js

然后在浏览器中打开 http://<您的 CVM IP 地址>:3000 并查看输出结果。

(如果访问不成功,可能是机器安全组禁用了 3000 端口所致,你可以前往控制台修改安全组配置。)

该步骤完成后,可使用 Ctrl + C 终止运行。

Express 应用生成器

任务时间:5min ~ 10min

安装 Express 应用生成器

通过应用生成器工具 express 可以快速创建一个应用的骨架。

通过如下命令安装:

npm install express-generator -g

-h 选项可以列出所有可用的命令行选项:

express -h

将得到输出:

  Usage: express [options] [dir]

  Options:

    -h, --help          output usage information
    -V, --version       output the version number
    -e, --ejs           add ejs engine support (defaults to jade)
        --hbs           add handlebars engine support
    -H, --hogan         add hogan.js engine support
    -c, --css <engine>  add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
        --git           add .gitignore
    -f, --force         force on non-empty directory

创建项目

进入工作目录:

cd /data/release

执行如下命令,在当前工作目录下创建一个命名为 myapp 的应用:

express myapp

完成后,点击查看 myapp 项目

生成的应用程序具有以下目录结构:

.
├── app.js
├── bin
│   └── www
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
│       └── style.css
├── routes
│   ├── index.js
│   └── users.js
└── views
    ├── error.pug
    ├── index.pug
    └── layout.pug

7 directories, 9 files

启动应用

进入该应用目录:

cd myapp

然后安装所有依赖包:

npm install

启动这个应用(MacOS 或 Linux 平台)[?]

DEBUG=myapp npm start

然后在浏览器中打开 http://<您的 CVM IP 地址>:3000 网址就可以看到这个应用了。

(该步骤完成后,可使用 Ctrl + C 终止运行。)

Windows 平台使用如下命令:set DEBUG=myapp & npm start

基本路由

任务时间:10min ~ 15min

Express 路由简介

路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等)组成的,涉及到应用如何响应客户端对某个网站节点的访问。

每一个路由都可以有一个或者多个处理器函数,当匹配到路由时,这些函数将被执行。

路由的定义由如下结构组成:

app.METHOD(PATH, HANDLER)

其中:

  • app 是一个 express 实例;
  • METHOD 是某个 HTTP 请求方式 中的一个
  • PATH 是服务器端的路径;
  • HANDLER 是当路由匹配到时需要执行的函数。

一个简单的 Express 路由

修改 hello 项目

返回开始创建的 hello 项目:

cd /data/release/hello

编辑 app.js,参考修改如下:

示例代码:/data/release/hello/app.js
var express = require('express');
var app = express();

// 对网站首页的访问返回 "Hello World!" 字样
app.get('/', function (req, res) {
  res.send('Hello World!');
});

// 网站首页接受 POST 请求
app.post('/', function (req, res) {
  res.send('Got a POST request');
});

// /user 节点接受 PUT 请求
app.put('/user', function (req, res) {
  res.send('Got a PUT request at /user');
});

// /user 节点接受 DELETE 请求
app.delete('/user', function (req, res) {
  res.send('Got a DELETE request at /user');
});

var server = app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

启动应用

node app.js

(该步骤完成后,可使用 Ctrl + C 终止运行。)

测试

你可以使用 curl 命令或 Postman 等工具进行测试。

如在本地终端执行:

curl -X POST http://<您的 CVM IP 地址>:3000
curl -X PUT http://<您的 CVM IP 地址>:3000/user
curl -X DELETE http://<您的 CVM IP 地址>:3000/user

静态文件

任务时间:5min ~ 10min

利用 Express 托管静态文件

通过 Express 内置的 express.static 可以方便地托管静态文件,例如图片、CSS、JavaScript 文件等。

创建静态目录

创建 public 目录:

mkdir -p /data/release/hello/public

在 public 目录下,创建 hello.html,然后复制下列代码到 hello.html 中:

示例代码:/data/release/hello/public/hello.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <h1>Hello World</h1>
</body>
</html>

修改应用

编辑 app.js,参考修改如下:

示例代码:/data/release/hello/app.js
var express = require('express');
var app = express();

app.use(express.static('public'));

var server = app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

我们在 app.js 中将静态资源文件所在的目录作为参数传递给 express.static 中间件,这样就可以提供静态资源文件的访问了。

启动应用

node app.js

在浏览器中打开 http://<您的 CVM IP 地址>:3000/hello.html 网址就可以看到这个文件了。

你还可以将本地的文件通过拖拽至左边目录树的 public 目录上传文件来测试。

假设在 public 目录放置了图片、CSS 和 JavaScript 文件,你就可以从浏览器中访问:

http://<您的 CVM IP 地址>:3000/images/kitten.jpg
http://<您的 CVM IP 地址>:3000/css/style.css
http://<您的 CVM IP 地址>:3000/js/app.js
http://<您的 CVM IP 地址>:3000/images/bg.png
http://<您的 CVM IP 地址>:3000/hello.html

static 中间件更多用法

多个目录

如果你的静态资源存放在多个目录下面,你可以多次调用 express.static 中间件:

app.use(express.static('public'));
app.use(express.static('files'));

访问静态资源文件时,express.static 中间件会根据目录添加的顺序查找所需的文件。

指定路径

如果你希望所有通过 express.static 访问的文件都存放在一个“虚拟(virtual)”目录(即目录根本不存在)下面,可以通过为静态资源目录指定一个挂载路径的方式来实现,如下所示:

编辑 app.js,参考修改如下:

示例代码:/data/release/hello/app.js
var express = require('express');
var app = express();

app.use('/static', express.static('public'));

var server = app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

启动应用:

node app.js

现在,你就可以通过带有 “/static” 前缀的地址来访问 public 目录下面的文件了。如:

http://<您的 CVM IP 地址>:3000/static/hello.html

完成

任务时间:1min

完成实验

恭喜!您已经完成了 Express 入门的全部实验内容!

更多 Express 相关资料,请查看 Express 官网 。

使用NIO进行快速的文件拷贝

  1. public static void fileCopy( File in, File out )
  2.             throws IOException
  3.     {
  4.         FileChannel inChannel = new FileInputStream( in ).getChannel();
  5.         FileChannel outChannel = new FileOutputStream( out ).getChannel();
  6.         try
  7.         {
  8. //          inChannel.transferTo(0, inChannel.size(), outChannel);      // original — apparently has trouble copying large files on Windows  
  9.             // magic number for Windows, 64Mb – 32Kb)  
  10.             int maxCount = (64 * 1024 * 1024) – (32 * 1024);
  11.             long size = inChannel.size();
  12.             long position = 0;
  13.             while ( position < size )
  14.             {
  15.                position += inChannel.transferTo( position, maxCount, outChannel );
  16.             }
  17.         }
  18.         finally
  19.         {
  20.             if ( inChannel != null )
  21.             {
  22.                inChannel.close();
  23.             }
  24.             if ( outChannel != null )
  25.             {
  26.                 outChannel.close();
  27.             }
  28.         }
  29.     }

列出文件和目录

  1. File dir = new File(“directoryName”);
  2.   String[] children = dir.list();
  3.   if (children == null) {
  4.       // Either dir does not exist or is not a directory  
  5.   } else {
  6.       for (int i=0; i < children.length; i++) {
  7.           // Get filename of file or directory  
  8.           String filename = children[i];
  9.       }
  10.   }
  11.   // It is also possible to filter the list of returned files.  
  12.   // This example does not return any files that start with `.’.  
  13.   FilenameFilter filter = new FilenameFilter() {
  14.       public boolean accept(File dir, String name) {
  15.           return !name.startsWith(“.”);
  16.       }
  17.   };
  18.   children = dir.list(filter);
  19.   // The list of files can also be retrieved as File objects  
  20.   File[] files = dir.listFiles();
  21.   // This filter only returns directories  
  22.   FileFilter fileFilter = new FileFilter() {
  23.       public boolean accept(File file) {
  24.           return file.isDirectory();
  25.       }
  26.   };
  27.   files = dir.listFiles(fileFilter);

创建ZIP和JAR文件 create zip jar

  1. import java.util.zip.*;
  2. import java.io.*;
  3. public class ZipIt {
  4.     public static void main(String args[]) throws IOException {
  5.         if (args.length < 2) {
  6.             System.err.println(“usage: java ZipIt Zip.zip file1 file2 file3”);
  7.             System.exit(-1);
  8.         }
  9.         File zipFile = new File(args[0]);
  10.         if (zipFile.exists()) {
  11.             System.err.println(“Zip file already exists, please try another”);
  12.             System.exit(-2);
  13.         }
  14.         FileOutputStream fos = new FileOutputStream(zipFile);
  15.         ZipOutputStream zos = new ZipOutputStream(fos);
  16.         int bytesRead;
  17.         byte[] buffer = new byte[1024];
  18.         CRC32 crc = new CRC32();
  19.         for (int i=1, n=args.length; i < n; i++) {
  20.             String name = args[i];
  21.             File file = new File(name);
  22.             if (!file.exists()) {
  23.                 System.err.println(“Skipping: “ + name);
  24.                 continue;
  25.             }
  26.             BufferedInputStream bis = new BufferedInputStream(
  27.                 new FileInputStream(file));
  28.             crc.reset();
  29.             while ((bytesRead = bis.read(buffer)) != –1) {
  30.                 crc.update(buffer, 0, bytesRead);
  31.             }
  32.             bis.close();
  33.             // Reset to beginning of input stream  
  34.             bis = new BufferedInputStream(
  35.                 new FileInputStream(file));
  36.             ZipEntry entry = new ZipEntry(name);
  37.             entry.setMethod(ZipEntry.STORED);
  38.             entry.setCompressedSize(file.length());
  39.             entry.setSize(file.length());
  40.             entry.setCrc(crc.getValue());
  41.             zos.putNextEntry(entry);
  42.             while ((bytesRead = bis.read(buffer)) != –1) {
  43.                 zos.write(buffer, 0, bytesRead);
  44.             }
  45.             bis.close();
  46.         }
  47.         zos.close();
  48.     }
  49. }

解析/读取XML 文件

  1. <?xml version=“1.0”?>
  2. <students>
  3.     <student>
  4.         <name>John</name>
  5.         <grade>B</grade>
  6.         <age>12</age>
  7.     </student>
  8.     <student>
  9.         <name>Mary</name>
  10.         <grade>A</grade>
  11.         <age>11</age>
  12.     </student>
  13.     <student>
  14.         <name>Simon</name>
  15.         <grade>A</grade>
  16.         <age>18</age>
  17.     </student>
  18. </students>
  1. package net.viralpatel.java.xmlparser;
  2. import java.io.File;
  3. import javax.xml.parsers.DocumentBuilder;
  4. import javax.xml.parsers.DocumentBuilderFactory;
  5. import org.w3c.dom.Document;
  6. import org.w3c.dom.Element;
  7. import org.w3c.dom.Node;
  8. import org.w3c.dom.NodeList;
  9. public class XMLParser {
  10.     public void getAllUserNames(String fileName) {
  11.         try {
  12.             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  13.             DocumentBuilder db = dbf.newDocumentBuilder();
  14.             File file = new File(fileName);
  15.             if (file.exists()) {
  16.                 Document doc = db.parse(file);
  17.                 Element docEle = doc.getDocumentElement();
  18.                 // Print root element of the document  
  19.                 System.out.println(“Root element of the document: “
  20.                         + docEle.getNodeName());
  21.                 NodeList studentList = docEle.getElementsByTagName(“student”);
  22.                 // Print total student elements in document  
  23.                 System.out
  24.                         .println(“Total students: “ + studentList.getLength());
  25.                 if (studentList != null && studentList.getLength() > 0) {
  26.                     for (int i = 0; i < studentList.getLength(); i++) {
  27.                         Node node = studentList.item(i);
  28.                         if (node.getNodeType() == Node.ELEMENT_NODE) {
  29.                             System.out
  30.                                     .println(“=====================”);
  31.                             Element e = (Element) node;
  32.                             NodeList nodeList = e.getElementsByTagName(“name”);
  33.                             System.out.println(“Name: “
  34.                                     + nodeList.item(0).getChildNodes().item(0)
  35.                                             .getNodeValue());
  36.                             nodeList = e.getElementsByTagName(“grade”);
  37.                             System.out.println(“Grade: “
  38.                                     + nodeList.item(0).getChildNodes().item(0)
  39.                                             .getNodeValue());
  40.                             nodeList = e.getElementsByTagName(“age”);
  41.                             System.out.println(“Age: “
  42.                                     + nodeList.item(0).getChildNodes().item(0)
  43.                                             .getNodeValue());
  44.                         }
  45.                     }
  46.                 } else {
  47.                     System.exit(1);
  48.                 }
  49.             }
  50.         } catch (Exception e) {
  51.             System.out.println(e);
  52.         }
  53.     }
  54.     public static void main(String[] args) {
  55.         XMLParser parser = new XMLParser();
  56.         parser.getAllUserNames(“c:\\test.xml”);
  57.     }
  58. }

使用iText JAR生成PDF

  1. import java.io.File;
  2. import java.io.FileOutputStream;
  3. import java.io.OutputStream;
  4. import java.util.Date;
  5. import com.lowagie.text.Document;
  6. import com.lowagie.text.Paragraph;
  7. import com.lowagie.text.pdf.PdfWriter;
  8. public class GeneratePDF {
  9.     public static void main(String[] args) {
  10.         try {
  11.             OutputStream file = new FileOutputStream(new File(“C:\\Test.pdf”));
  12.             Document document = new Document();
  13.             PdfWriter.getInstance(document, file);
  14.             document.open();
  15.             document.add(new Paragraph(“Hello Kiran”));
  16.             document.add(new Paragraph(new Date().toString()));
  17.             document.close();
  18.             file.close();
  19.         } catch (Exception e) {
  20.             e.printStackTrace();
  21.         }
  22.     }
  23. }