前言

如何安装node.js,如何搭建一个简易的http服务器我这里就不再赘述了,不懂的同学可以先去学习一下。当然了,我写的也就属于简易版的增强版,大家有什么高见的欢迎提出,然后进入正题。

目录结构

|-server.js
|-router.js
|-test.html
|-css|-test.css
|-js |-test.js

server.js

//原生模块
var http = require('http');
var fs = require('fs');
var url = require('url');
//自定义模块
var router = require('./router.js');
http.createServer(function(request,response){
//获取客户端访问的路径
var pathname = url.parse(request.url).pathname;
//如果用户只输入域名就改变访问路径,并发送主页的内容给客户端
if(pathname == "/"){
pathname = "/index.html";
}
  //获取当前请求客户端的IP地址
var ipv4 = get_client_ipv4(request);
//输出日志到控制台
showLog(ipv4,("请求"+decodeURI(pathname)));
//判断文件是否存在
fs.exists(__dirname + decodeURI(pathname),function(exists){
if(exists){
//使用router模块的函数
router.readFileBySuffixName(pathname,fs,request,response);
}else{
console.log(decodeURI(pathname)+"文件不存在!");
//文件不存在,向客户端发送404状态码,并发送该文件不存在的字符串
response.writeHead(404,{"Content-Type":"text/plain"});
response.end(pathname+"文件不存在!");
}
});
}).listen(80); //监听80端口 console.log('web服务已运行!'); /**
* @desc 获取IPV4地址
* @param req htttp.request
* @return string 32位IP地址
*/
function get_client_ipv4(req) {
//获取任意浏览器的IP地址,
var ip = req.headers['x-forwarded-for'] ||
req.ip ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress || '';
//获取到的IP地址中存在IPV4和IPV6的地址,我们只需要IPV4的地址
if(ip.split(',').length>0){
ip = (ip.split(',')[0]).match(/(\d+\.\d+\.\d+\.\d+)/)[0];
}
return ip;
}; /**
* @desc 向控制台输出日志,自动在头部添加时间、地址
* @param ipv4 string
* @param message string
*/
function showLog(ipv4,message){
//获取当前时间
var date = new Date();
//转换为本地时间的字符串形式并输入到控制台
console.log(date.toLocaleDateString() + " " + date.toLocaleTimeString() +
" " + ipv4 + " " + message);
}

首先引入模块,使用http.createServer创建http服务器,并监听80端口;http.createServer的回调函数接收两个值,一个request请求对象,一个response响应对象,request对象可以获取到客户端请求的信息,response对象用来返回数据到客户端;上面创建了两个简单的工具函数,分别用来获取客户端的IPV4地址、向控制台输出日志;使用fs.exists函数判断客户端请求的文件是否存在,如果不存在则返回404状态码,如果存在,则使用下面router.js中创建的readFileBySuffixName函数,读取相应的文件并根据后缀名设置响应头,然后发送数据到客户端。

router.js

/**
* @desc 根据后缀名读取文件
* @param pathname string 文件路径 url.parse(request.url).pathname
* @param fs fs
* @param request htttp.request
* @param response https.response
*/
exports.readFileBySuffixName = function(pathname,fs,request,response){
var ext = pathname.match(/(\.[^.]+|)$/)[0];//取得后缀名
switch(ext){ //根据后缀名读取相应的文件,设置响应头,并发送到客户端
case ".css":
case ".js":
//读取文件
fs.readFile("."+request.url,'utf-8',function(err,data){
if(err) throw err;
response.writeHead(200,{ //根据不同的后缀设置不同的响应头
"Content-Type":{
".css":"text/css",
".js":"application/javascript",
}[ext]
});
response.write(data); //发送文件数据到客户端
response.end(); //发送完成
});
break;
//jpg、gif、png后缀的图片
case ".jpg":
case ".gif":
case ".png":
//二进制读取文件
fs.readFile("."+decodeURI(request.url),'binary',function(err,data){
if(err)throw err;
response.writeHead(200,{
"Content-Type":{
".jpg":"image/jpeg",
".gif":"image/gif",
".png":"image/png",
}[ext]
});
response.write(data,'binary'); //发送二进制数据
response.end();
});
break;
case ".mp4":
//读取文件的状态
fs.stat('.'+decodeURI(request.url),function(err,stats){
if(err){
if(err.code === 'ENOENT'){
return response.sendStatus(404);
}
response.end(err);
}
//断点续传,获取分段的位置
var range = request.headers.range;
if(!range){
//206状态码表示客户端通过发送范围请求头Range抓取到了资源的部分数据
//416状态码表示所请求的范围无法满足
return response.sendStatus(416);
}
//替换、切分,请求范围格式为:Content-Range: bytes 0-2000/4932
var positions = range.replace(/bytes=/,"").split("-");
//获取客户端请求文件的开始位置
var start = parseInt(positions[0]);
//获得文件大小
var total = stats.size;
//获取客户端请求文件的结束位置
var end = positions[1] ? parseInt(positions[1],10):total -1;
//获取需要读取的文件大小
var chunksize = (end-start) + 1; response.writeHead(206,{
"Content-Range":"bytes "+ start+"-"+end+"/"+total,
"Accept-Ranges":"bytes",
"Content-Length":chunksize,
"Content-Type":"video/mp4"
});
//创建读取流
var stream = fs.createReadStream('.'+decodeURI(request.url),{start:start,end:end})
  .on("open",function(){
stream.pipe(response); //读取流向写入流传递数据
}).on("error",function(err){
response.end(err);
});
});
break;
      case ".rar":
//同步读取文件状态
  var stats = fs.statSync("." + decodeURI(request.url));
response.writeHead(200,{
"Content-Type": "application/octet-stream", //相应该文件应该下载
//模板字符串
"Content-Disposition": `attachment; filename = ${pathname.replace("/","")}`,
"Content-Length":stats.size
});
//管道流
fs.createReadStream("." + decodeURI(request.url)).pipe(response);
break;
        //以上都不匹配则使用默认的方法
      default:
       fs.readFile('.'+pathname,'utf-8',function(err,data){
response.writeHead(200,{
"Content-Type":"text/html"
});
response.write(data);
response.end();
});
}
}

router.js文件中只有一个readFileBySuffixName函数,该函数的作用是判断客户端访问文件的后缀名,css、js、图片、mp4视频、rar文件等都能成功返回到客户端;其中视频和下载文件使用流传输;因为如果不使用流的话,服务器要先缓存文件,然后再发送文件到客户端;使用HTML5视频的客户端会发送一个Content-Range的值到服务器,服务器根据这个range值读取一个文件指定的部分,并返回这个特定的部分数据到客户端,就实现了视频的断点续传,你可以随意的跳转到视频的任意一部分了!

进入项目文件夹,输入

node ./server.js

服务器端输出日志和测试页面

favicon.ico文件是该页面的图标文件,第一次进入页面浏览器会自动请求。

使用原生node.js搭建HTTP服务器,支持MP4视频、图片传输,支持下载rar文件的更多相关文章

  1. 使用 Node.js 搭建 Web 服务器

    使用Node.js搭建Web服务器是学习Node.js比较全面的入门教程,因为实现Web服务器需要用到几个比较重要的模块:http模块.文件系统.url解析模块.路径解析模块.以及301重定向技术等, ...

  2. 学习 node.js 搭建web服务器

    开始 学习使用 node.js 首先完成搭建一个 web服务器.myweb.js var http = require('http'); var url = require('url'); var h ...

  3. 用node.js搭建本地服务器

    我的第一篇笔记来写写node.js,我对node.js的并不是很了解,基本的项目路径变换还是会的.原先我下载node.js就是我想学vue.js,后来因为工作的繁忙搁浅了我的计划.最近在学习phase ...

  4. node.js搭建Web服务器

    Node.js 博客搭建 一. 学习需求 Node 的安装运行 会安装node,搭建node环境 会运行node. 基础模块的使用 Buffer:二进制数据处理模块 Event:事件模块 fs:文件系 ...

  5. node.js搭建https服务器

    HTTPS简介 HTTPS:(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版. ...

  6. Node.js学习笔记(五) --- 使用Node.js搭建Web服务器

    1. Node.js 创建的第一个应用 1.引入http模块 var http = require("http"); 2. 创建服务器接下来我们使用 http.createServ ...

  7. express+node.js搭建的服务器和在sublimeServer下的页面请求报跨域错误

    1.前端页面使用vue中的axios请求nodejs响应.报以下错误: Failed to load http://localhost:3000/users/validate: Response to ...

  8. 使用node.js搭建本地服务器

    第一步安装node:https://nodejs.org/zh-cn/download/ 接下来就需要安装http的镜像文件 打开cmd:输入以下命令 npm install http-server ...

  9. 如何使用Node.js搭建一个服务器

    在node环境中运行下面的代码 "use strict"; const http = require("http"), path = require(" ...

随机推荐

  1. [Python]正则匹配字符串 | 蒲公英二维码图片url

    代码示例: import re def Find(string): # findall() 查找匹配正则表达式的字符串 url = re.findall('http[s]?://(?:[a-zA-Z] ...

  2. 你应该知道的 MySQL 的锁

    背景 数据库的锁是在多线程高并发的情况下用来保证数据稳定性和一致性的一种机制.MySQL 根据底层存储引擎的不同,锁的支持粒度和实现机制也不同.MyISAM 只支持表锁,InnoDB 支持行锁和表锁. ...

  3. Spring策略模式的实现

    场景: 有时候一个业务方法有多个实现类,需要根据特定的情形进行业务处理. 例如:商店支付,我们可以使用支付宝.微信扫描农行.xxx行的快捷支付(而不是微信支付.支付宝支付二维码)购买商品. 实现代码( ...

  4. 为lumen添加session支持

    为lumen添加session支持,同时配置全局函数csrf_token可用 首先laravel和lumen框架的版本要一致,我这里版本都是5.4 1.复制laravel框架config目录下的ses ...

  5. android#boardcast#发送自定义广播

    广播主要分为两种类型,标准广播和有序广播,通过实践的方式来看下这两种广播具体的区别. 一.发送标准广播 在发送广播之前,我们还是需要先定义一个广播接收器来准备接收此广播才行,不然发出去也是白发.因此新 ...

  6. Linux命令---ln、readlink

    ln 无参数--------创建硬链接 -s  -------------创建软链接 用法:ln  [option]  源文件  目标文件 ln test.txt test_hard.txt 只有在同 ...

  7. 深入理解MySQL索引原理和实现——为什么索引可以加速查询?

    说到索引,很多人都知道“索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址,在数据十分庞大的时候,索引可以大大加快查询的速度,这是因为使用索引后可以不用扫描全表来定位某 ...

  8. PTA (Advanced Level)1082.Read Number in Chinese

    Given an integer with no more than 9 digits, you are supposed to read it in the traditional Chinese ...

  9. 【转帖】比df命令更有用的磁盘信息工具

    比df命令更有用的磁盘信息工具 http://embeddedlinux.org.cn/emb-linux/entry-level/201310/30-2666.html 除了df fdisk 还有这 ...

  10. 重载(overload)和重写(override)的区别

    方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性. 重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同.参数个数不同或者二者都不同)则 ...