欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-06-23 23:12:19
未使用express等框架,仅使用原生模块和一些自定义模块搭建HTTP服务器,并支持MP4格式的视频传输和jpg、gif、png的图片传输;该node.js服务器提供rar文件下载功能。路由模块通过请求的后缀名将不同类型的文件分发到客户端,并设置相应的响应头。为了监控服务器的运行状况,我添加了获取客... ......

前言

如何安装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

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

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

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