安装npm及cnpm(Windows)
【工具官网】
Node.js : http://nodejs.cn/
淘宝NPM: https://npm.taobao.org/
【安装步骤】
一、安装node.js
- 前往node.js官网下载并安装工具,这里安装路径选到D盘,D:\Program Files\nodejs
安装完毕在命令行输入以下命令测试是否安装成功,正确会出现版本号npm -v
- 改变原有的环境变量,
(1)我们要先配置npm的全局模块的存放路径以及cache的路径,例如我希望将以上两个文件夹放在NodeJS的主目录下,便在NodeJs下建立"node_global"及"node_cache"两个文件夹,输入以下命令改变npm配置npm config set prefix "D:\Program Files\nodejs\node_global" npm config set cache "D:\Program Files\nodejs\node_cache"
(2)在系统环境变量添加系统变量NODE_PATH,输入路径D:\Program Files\nodejs\node_global\node_modules,此后所安装的模块都会安装到改路径下
(3)在命令行输入以下命令试着安装express(注:"-g"这个参数意思是装到global目录下,也就是上面说设置的"D:\Program Files\nodejs\node_global"里面。)npm install express -g
安装完毕后可以看到.\node_global\node_modules\express 已经有内容
(4)在命令行输入node进入编辑模式,输入以下代码测试是否能正常加载模块:require('express')
假设成功,可以看到有输出。假设出错,检查NODE_PATH的路径。
二、安装淘宝npm(cnpm)
- 安装cnpm
(1)输入以下命令npm install -g cnpm --registry=https://registry.npm.taobao.org
(2)输入cnpm -v输入是否正常,这里肯定会出错。
cnpm -v
(3)添加系统变量path的内容
因为cnpm会被安装到D:\Program Files\nodejs\node_global下,而系统变量path并未包含该路径。在系统变量path下添加该路径即可正常使用cnpm。
三、简易漫画网站搭建-漫画喵Server版
转载自:https://www.miaoerduo.com/2017/03/14/cartoon-cat-server/
前端的话,使用标准的 Html,Css 和 Js 就可以。后台是个 API Server,Python + Flask 或者 Nodejs + Express 都是不错的选择(使用起来很简单。。。),小喵这里就选用 Nodejs + Express 的方式编写后台(一直写Js就可以了 :P )。另外,大型的漫画网站,漫画的信息应该会存到数据库中,这样会方便查找和管理。这里考虑到漫画数目比较少,就去掉了数据库这一步骤,直接通过文件操作来得到漫画信息,工作量也大大减少了。
一、目录结构
下面是小喵的目录结构:
$ tree blog -N -L 2
blog
├── README.md
├── main.js
├── package.json
└── public
├── api.html
├── cartoon.html
├── chapter.html
├── css
├── img
├── index.html
├── js
└── store
main.js 这里就是后台API的程序。因为功能很简单,所以就放在一个文件中。package.json 是nodejs的包管理器,在这里可以定义依赖。我们这里只依赖Express。public 这个文件夹用来存放静态的资源,包括Html、Css、js、image以及漫画资源(store)。
二、后台程序
- 依赖安装
原生的 Nodejs 就已经十分适合写API程序了,Express 只是让它更加的方便了而已(至少对于这个项目来说)。
Nodejs 的安装十分简单,在官网上 https://nodejs.org 有下载链接,服务器上使用 apt-get 等工具也很容易安装。
Express 的话,可以使用 npm install express —save 来安装。这里小喵使用的 package 的方式来安装。在项目根目录新建 package.json 文件,写入配置信息:
{
"name": "cartoon-cat-server",
"version": "0.0.1",
"dependencies": {
"express": "visionmedia/express"
}
}
之后使用 npm install 命令就可以完成安装。安装完成之后会发现根目录多了一个 node_modules 文件夹,里面就是我们的依赖库了。喵粉们如果下载了我的这个项目的话,第一步也是要进入项目目录然后输入 npm install 。
- 漫画的文件结构
我们的漫画资源都是通过 漫画喵 这个爬虫工具下载下来的,因此漫画都是每个章节都是一个文件夹,每个章节的漫画图片都放在对应的文件夹中,而且按照页码来命名。
这样通过遍历文件夹似乎就能获取漫画的信息了!
漫画列表和章节中的图片列表都可以通过上述的方式来解决,但是章节的列表却不行。因为漫画的章节有时候并没有明确的顺序(比如突然出现一个番外篇啥的),这样遍历文件夹默认的顺序(按名称)就可能是错误的。 我们有两个解决办法:
按照文件夹的创建时间来显示文件名。这样有点不灵活。
在每个漫画的根目录建一个文本文件,用来存放章节的信息。
小喵选择第二种策略,创建这个list的方法十分简单粗暴,在漫画目录下面使用:
ls -t -r > index
ls 是linux上面的显示目录的工具,-t 表示按时间排序(最上面是最新的),-r 表示倒序,> 是重定向,最终输出到 index 这个文件。然后编辑这个文件,删掉 index 这一行(系统貌似是先生成 index 这个文件,然后再执行 ls,最后把结果输入到文件中,因此文件里面多了一个 index 的文件名),再做一些必要的调整。
Windows上可以使用:
dir /OD /B > index
dir 是windows的查看目录的命令,/OD 表示按照时间排序,/B 表示只显示文件名,> 重定向到 index。windows 上的这个列表文件中也会出现 index 这个文件名(看来各种操作系统都一样)。另外需要注意的是 windows 的换行和 linux 或 mac 不一样。
这样,我们就可以通过读这个 index 文件来获取章节的信息了。
最终的漫画的结构(为了显示的方便,删除了很多图片和章节)如下:
$ tree store -N -L 3
store
├── 犬夜叉
│ ├── index
│ ├── 第1章
│ │ ├── 00001.jpg
│ │ └── 00002.jpg
│ └── 第2章
│ ├── 00001.jpg
│ └── 00002.jpg
└── 极黑的布伦希尔特
├── index
├── 第1章
│ ├── 00001.jpg
│ └── 00002.jpg
└── 第2章
├── 00001.jpg
└── 00002.jpg
- API 编写
Express 十分的容易使用。这里小喵给一个官网的Hello World的教程让大家看一下:
var express = require('express')
var app = express()
app.get('/', function (req, res) {
res.send('Hello World!')
})
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
将上述代码保存成 main.js,使用 node main.js 就可以启动这个程序,然后用浏览器访问 http://localhost:3000 ,就能看到一个显示着 hello world 的页面。
require 语句用来引入依赖,app 是express的封装的对象。
通过 app.get 方法就可以给指定的url(官方说法叫route)绑定相应的处理方法(GET方法的请求)。处理函数有2个参数 req 表示request,也就是用户的请求,通过这个对象我们可以获取用户的输入的参数,res 表示response,是一个向用户返回数据的对象。
listen 用来监听一个端口启动服务。
这里小喵先给出自己定义的一些辅助的函数,定义错误信息和参数校验,后面会使用到:
// 引入依赖
var express = require('express');
var fs = require("fs"); // 即file system,用来进行文件操作
var app = express();
/**
* 错误提示
*/
var ErrorHelper = {
'internal_error': function () {
return {
'msg': 'something wrong with server',
'code': 1
};
},
'missing_param': function (param) {
return {
'msg': 'missing param: ' + param,
'code': 2
};
},
'error_param': function (param, data) {
return {
'msg': 'the param ' + param + '(' + data + ') is illegal',
'code': 3
}
},
'not_found': function (param) {
return {
'msg': 'cannot find ' + param,
'code': 4
};
}
};
/**
* 检查参数格式,只能输入字母,数字和汉字
*/
function checkParam(param) {
return /^[\u4e00-\u9fa5_a-zA-Z0-9]+$/.test(param);
}
(1)get_cartoon_list
这个接口用来获取所有的漫画列表。
/**
* 获取漫画列表
*/
app.get('/get_cartoon_list', function (req, res) {
fs.readdir(__dirname + '/public/store', function (err , files) {
if (err) {
res.jsonp(ErrorHelper.internal_error());
}
res.jsonp({'cartoon': files, 'code': 0});
});
});
这个函数十分的简单,通过 fs 读取store中的文件名,然后用json的格式返回回去。这里小喵用的jsonp,为了解决跨域请求的问题,不过我们的页面和服务是一台机器的,所以这部分并不需要。
(2) get_chapter_list
这个接口用来获取漫画的章节的信息,所以需要输入参数,这里定为cartoon。
/**
* 获取章节信息
*/
app.get('/get_chapter_list', function (req, res) {
var cartoon = req.query.cartoon;
if (!cartoon) {
res.jsonp(ErrorHelper.missing_param('cartoon'));
return;
}
if (!checkParam(cartoon)) {
res.jsonp(ErrorHelper.error_param('cartoon', cartoon));
return;
}
var cartoon_dir = __dirname + '/public/store/' + cartoon;
fs.exists(cartoon_dir + '/index', function (exists) {
if (!exists) {
res.jsonp(ErrorHelper.not_found(cartoon));
return;
}
fs.readFile(cartoon_dir + '/index', function (err, data) {
if (err) {
res.jsonp(ErrorHelper.internal_error());
return;
}
var chapter_list = data.toString().split('\n').filter(function (d) {
return d.length > 0;
});
res.jsonp({'chapter': chapter_list, 'code': 0});
});
});
});
首先判断输入的参数,之后判断对应漫画的文件夹中是否有index这个文件,如果有的话就读取然后返回给用户。
(3) get_img_list
这个接口用来返回漫画的具体章节的图片的URL,用户需要输入漫画名(cartoon)和章节名(chapter)。注意要修改自己的HOST的地址。
var HOST = "localhost"; // 如果不是在本机上使用,请改成实际的ip地址
// 后面的图片的URL会使用这个变量来构造
var PORT = 3000;
app.get('/get_img_list', function (req, res) {
var cartoon = req.query.cartoon;
if (!cartoon) {
res.jsonp(ErrorHelper.missing_param('cartoon'));
return;
}
if (!checkParam(cartoon)) {
res.jsonp(ErrorHelper.error_param('cartoon', cartoon));
return;
}
var chapter = req.query.chapter;
if (!chapter) {
res.jsonp(ErrorHelper.missing_param('chapter'));
return;
}
if (!checkParam(chapter)) {
res.jsonp(ErrorHelper.error_param('chapter', chapter));
return;
}
var cartoon_dir = __dirname + '/public/store/' + cartoon;
fs.exists(cartoon_dir + '/index', function (exists) {
if (!exists) {
res.jsonp(ErrorHelper.not_found(cartoon));
return;
}
fs.readdir(cartoon_dir + '/' + chapter, function (err, images) {
if (err) {
res.jsonp(ErrorHelper.error_param('chapter', chapter));
return;
}
// 按名字排序
images.sort(function (lhs, rhs) {
return parseInt(lhs.split('.')[0]) - parseInt(rhs.split('.')[0]);
});
var urls = images.map(function (image) {
return 'http://' + HOST + ':' + PORT + '/store/' + cartoon + '/' + chapter + '/' + image;
});
res.jsonp({'img': urls, 'code': 0});
});
});
});
这是目前最复杂的函数了,先检查参数,然后判断漫画是否存在,再判断章节是否存在,列出章节文件夹里面的图片名,并按数字的顺序排序。最终构造成URL,返回给用户。
(4) 静态资源
public文件夹中的资源都是静态资源,用户可以通过URL访问。在这里Nodejs也是支持的:
app.use('/', express.static('public'));
不过Nodejs本身并不适这种静态资源的工作,所以如果是生产环境中,建议大家还是使用Nginx等工具,让Nodejs安心的处理业务逻辑吧。
(5) 启动服务
var server = app.listen(PORT, function () {
console.log("应用实例,访问地址为 http://%s:%s", HOST, PORT);
});
三、前端
- Ajax
前端使用Ajax就可以很容易完成,相信即使是前端小白也能实现,而且还比小喵做的好看(无奈脸)。小喵使用了JQuery 来处理Ajax的内容,界面库使用了 Metro,然而即使这样也没有提高网站的颜值。
源码可以从github上下载到,所以小喵就不重点介绍前端了。
- 懒加载
有一点需要注意,在漫画图片的页面中,通常会出现大量的图片,如果只是简单的使用 img 标签的话,可能会导致浏览器同时加载所有的图片,如果网速不好的话,我们的体验也会相当的差(局域网请无视)。所以我们使用一种 懒加载 的策略,只有可见的图片才会加载。然后小喵就从github上找相关的插件,然后发现了一个使用比较方便的代码,还有详细的原理介绍,感兴趣的话大家可以看一下。
图片懒加载插件实战:http://www.cnblogs.com/beidan/p/5648240.html
插件的github:https://github.com/beidan/lazeLoadImg
四、写在后面
至此,我们就搭建好了一个可以随时玩耍的个人漫画网站了。喵粉们感兴趣可以star、fork这个项目,如果喜欢开发的话,能帮忙一起提高网站的颜值就更好了 O(∩_∩)O哈!
项目地址:https://github.com/miaoerduo/cartoon-cat-server ,欢迎大家随时star、fork和指教。
PS. 请搭配漫画喵爬虫版一起食用:https://github.com/miaoerduo/cartoon-cat
使用的话,按如下的流程:
git clone git@github.com:miaoerduo/cartoon-cat-server.git
cd cartoon-cat-server
npm install
node main.js
另外,为了避免程序突然崩掉,建议大家使用 forever 这个工具。上面的流程的最后一句 node main.js 就可以改成下面的。
npm install forever -g
forever start main.js
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。