用nodejs写个简单的视频网站[教程]#
首先你要有nodejs环境和会使用npm,不会自行Google或百度。
ok!开始吧!
![img_0140a4ea72b76284d886e9161fe6c558.gif](https://yqfile.alicdn.com/img_0140a4ea72b76284d886e9161fe6c558.gif?x-oss-process=image/resize,w_1400/format,webp)
效果示例
第一步 用nodejs编写后台:
1.新建项目
2.创建static文件夹(可自行更改)
3.static文件夹下创建videos文件夹(可自行更改)
4.在项目目录创建 app.js 内部代码如下
var http = require('http');
var fs = require('fs');
var rd = require('rd');
var express = require('express');
var app = express();
app.use("/static", express.static(__dirname + '/static'));//引入静态文件夹
var vdo_path = __dirname + "\\static\\videos\\";//视频文件夹路径(可自行更改)
var vdo_info_ls = [];//获取到的文件信息集
function getFileInfo(path) {//遍历文件夹
try {
var files = rd.readSync(path);//获取目录下所有文件和文件夹
for (var i in files) {//循环遍历
if (!fs.lstatSync(files[i]).isDirectory()) {//判断是否为文件
if (files[i].toLowerCase().split(".mp4").reverse()[0].length== 0) {//判断是否为MP4格式文件(这里默认以MP4为例 其他格式大家自行过滤)
vdo_info_ls[vdo_info_ls.length]={
name: files[i].split("\\").reverse()[0].replace(".mp4", "").replace(".MP4", ""),//获取文件名
url: (vdo_path.replace(__dirname, "")+ files[i].replace(vdo_path, "")).replace("\\", "/"), //获取文件的web路径
mtime: fs.statSync(files[i]).mtime//修改时间作为发布时间
}//添加信息到文件信息集
}
}
}
}
catch (e) {
console.log(e)
}
}
function reGetFileInfos() {//这里是为了大家以后写后台进行文件刷新时使用
vdo_info_ls = [];//初始化集合
getFileInfo(vdo_path); //遍历文件夹
vdo_info_ls.sort(function (a, b) {//时间排序
return Date.parse(b.ctime) - Date.parse(a.ctime);//时间正序(不过这个方法好像只能对月日起效 对年好像不起作用)
});
}
var page_count = 20;//分页条数
app.get('/getvdojson', function (req, res) {
var ret = [];//返回的分页json初始化
if (req.query.page) {//判断是否有get参数page
if (parseInt(req.query.page) >= 0) {//
for (var i = 0; i < page_count; i++) {//遍历获取
ret[ret.length] = vdo_info_ls[parseInt(req.query.page) * page_count + i];
}
}
}
res.json(ret);//返回json
});
// 创建服务端
http.createServer(app).listen('5000', function () {
reGetFileInfos();//初始化文件信息集
console.log('启动服务器完成');
});
写入代码后运行。
浏览器访问http://127.0.0.1:5000/getvdojson?page=0 查看效果
改变url的page参数查看效果。
如果运行正常页面会返回json。(如:[{“name”:”一分钟学唱《loser》…..”mtime”:”2016-07-27T11:11:28.501Z”}])
第二步 编写网站前台主页面:
1.添加以下代码到app.js
app.get('/', function (req, res) {
res.sendFile(__dirname + "\\" + "index.html");
});
2.static文件夹下创建img,js和css文件夹分别存放img,js和css文件
3.在项目下创建index.html写入以下代码
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<title>小视频</title>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/minigrid.js"></script><!-- 轻量级瀑布流插件 具体可搜索minigrid了解 -->
<style>
/*全局样式*/
* {
margin: 0;
padding: 0;
}
a {
text-decoration: none;
}
body {
line-height: 1;
background: #FFF;
}
body {
font-family: "Microsoft Yahei";
background: #f4f4f4;
min-width: 1000px;
height: 100%;
}
/*主体居中*/
.container {
width: 98%;
position: relative;
margin-bottom: 20px;
margin: 0 auto;
}
#photo-box .photo {
display: block;
/*float: left;*/
width: 225px;
background-color: #fff;
overflow: hidden;
position: absolute;
-webkit-transition: .3s ease-in-out;
-o-transition: .3s ease-in-out;
transition: .3s ease-in-out;
border: 1px solid #e2e2e2;
}
#photo-box .photo .content {
margin: 0 10px ;
font-size: 14px;
color: #777;
line-height: 20px;
word-break: break-all;
}
/*显示加载*/
#load-over{
width: 95%;
margin: 0 auto;
border-bottom: 1px #ccc solid;
border-top: 1px #ccc solid;
padding: 20px 0px;
text-align: center;
margin-bottom: 30px;
color: #999;
}
</style>
</head>
<body>
<!--"photo-box"为瀑布流 图片容器-->
<div id="photo-box" class="container">
<!--"photo"显示视频缩略图与图片信息-->
<a class="photo" target="_blank" href="#">
[站外图片上传中……(2)]
<div class="content">
欢迎来到XXX(公告或广告位)
</div>
</a>
<a class="photo" target="_blank" href="#">
[站外图片上传中……(3)]
<div class="content">
欢迎来到XXX(公告或广告位)
</div>
</a>
<a class="photo" target="_blank" href="#">
[站外图片上传中……(4)]
<div class="content">
欢迎来到XXX(公告或广告位)
</div>
</a>
<a class="photo" target="_blank" href="#">
[站外图片上传中……(5)]
<div class="content">
欢迎来到XXX(公告或广告位)
</div>
</a>
<a class="photo" target="_blank" href="#">
[站外图片上传中……(6)]
<div class="content">
欢迎来到XXX(公告或广告位)
</div>
</a>
<a class="photo" target="_blank" href="#">
[站外图片上传中……(7)]
<div class="content">
欢迎来到XXX(公告或广告位)
</div>
</a>
<a class="photo" target="_blank" href="#">
[站外图片上传中……(8)]
<div class="content">
欢迎来到XXX(公告或广告位)
</div>
</a>
<a class="photo" target="_blank" href="#">
[站外图片上传中……(9)]
<div class="content">
欢迎来到XXX(公告或广告位)
</div>
</a>
<a class="photo" target="_blank" href="#">
[站外图片上传中……(10)]
<div class="content">
欢迎来到XXX(公告或广告位)
</div>
</a>
<a class="photo" target="_blank" href="#">
[站外图片上传中……(11)]
<div class="content">
欢迎来到XXX(公告或广告位)
</div>
</a>
<a class="photo" target="_blank" href="#">
[站外图片上传中……(12)]
<div class="content">
欢迎来到XXX(公告或广告位)
</div>
</a>
<a class="photo" target="_blank" href="#">
[站外图片上传中……(13)]
<div class="content">
欢迎来到XXX(公告或广告位)
</div>
</a>
</div>
<div id="load-over" style="">
加载中...
</div>
<script>
minigrid('#photo-box', '.photo', 20, null,
function() {
//更新后回调
});//瀑布流插件初始化
window.addEventListener('resize', function() {//窗口大小改变自动排版
minigrid('#photo-box', '.photo');
});
</script>
</body>
</html>
访问http://127.0.0.1:5000/ 查看效果
效果示例:
![img_72c321e5db975a5c413472dc5cf0a30a.png](https://yqfile.alicdn.com/img_72c321e5db975a5c413472dc5cf0a30a.png?x-oss-process=image/resize,w_1400/format,webp)
Paste_Image.png
第三步 前后台结合:
1.修改index.html为以下代码(请注意修改变化)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>小视频</title>
<script src="/static/js/jquery-2.1.4.min.js"></script>
<script src="/static/js/minigrid.js"></script>
<style>
/*全局样式*/
* {
margin: 0;
padding: 0;
}
a {
text-decoration: none;
}
body {
line-height: 1;
background: #FFF;
}
body {
font-family: "Microsoft Yahei";
background: #f4f4f4;
min-width: 1000px;
height: 100%;
}
/*主体居中*/
.container {
width: 98%;
position: relative;
margin-bottom: 20px;
margin: 0 auto;
}
#photo-box .photo {
display: block;
/*float: left;*/
width: 225px;
background-color: #fff;
overflow: hidden;
position: absolute;
-webkit-transition: .3s ease-in-out;
-o-transition: .3s ease-in-out;
transition: .3s ease-in-out;
border: 1px solid #e2e2e2;
}
#photo-box .photo .content {
margin: 0 10px;
font-size: 14px;
color: #777;
line-height: 20px;
word-break: break-all;
}
/*显示加载*/
#load-over {
width: 95%;
margin: 0 auto;
border-bottom: 1px #ccc solid;
border-top: 1px #ccc solid;
padding: 20px 0px;
text-align: center;
margin-bottom: 30px;
color: #999;
}
</style>
<script>
//--获取视频缩略图--
function vload(obj) {
$(obj).removeAttr("poster");
var vimg = $("<img/>",{width:"100%"})[0];
captureImage(obj, vimg);
$(obj).after(vimg);
obj.remove();
minigrid('#photo-box', '.photo');
};
var scale = 0.8; //缩放
function captureImage(video, output) { //截图
try {
var videocanvas = $("<canvas/>")[0];
videocanvas.width = video.videoWidth * scale;
videocanvas.height = video.videoHeight * scale;
videocanvas.getContext('2d').drawImage(video, 0, 0, videocanvas.width, videocanvas.height);
output.src = videocanvas.toDataURL("image/png");
delete videocanvas;
} catch(e) {
output.src = "/static/img/status.gif"; //--status.gif为加载动画
}
};
</script>
</head>
<body>
<!--"photo-box"为瀑布流 图片容器-->
<div id="photo-box" class="container">
<!--"photo"显示视频缩略图与图片信息-->
<a class="photo" target="_blank" href="#">
[站外图片上传中……(14)]
<div class="content">
欢迎来到XXX(公告或广告位)
</div>
</a>
</div>
<div id="load-over" style="">
加载中...
</div>
<script>
minigrid('#photo-box', '.photo', 20, null,
function() {
//更新后回调
});//瀑布流插件初始化
window.addEventListener('resize', function() {//窗口大小改变自动排版
minigrid('#photo-box', '.photo');
});
function b_load(page){
$("#load-over").text("加载...");
$.getJSON("/getvdojson?page=" + page, function(data) {
if(data.length > 0) {
for(let i in data) {
if(data[i]==null){
$("#load-over").text("加载完毕");
continue;
}
$("#photo-box").append(
$("<a/>", {
class: "photo",
href:"javascript:void(0)"
})
.append($("<video/>", { src: data[i].url , preload: "metadata", onloadeddata:'vload(this)',poster:"/static/img/status.gif",width: "100%"
}))
.append($("<div/>", { class: "content" }).html(data[i].name) )
.append($("<input/>",{type:"datetime-local",value:(data[i].mtime).split('.')[0],readonly:"readonly"
}).css({width:"100%",
border:"none",
color: "#777",
"padding-left": "10px",
"padding-bottom": "10px"
}))
);
}
minigrid('#photo-box', '.photo');
}
});
}
page = 0;
window.onscroll = function() {
// var scrolltop=document.documentElement.scrollTop||document.body.scrollTop;
var tops = $(document).scrollTop(); //获取滚动条的位置
var sctop = $(document).height() - $(window).height();
if(tops >= sctop) //成立说明滚动条已在最底部
{
page = page + 1; //传递页码
b_load(page);
}
};
b_load(page);
page=page+1;
b_load(page);
</script>
</body>
</html>
[为什么不直接显示视频而偏偏搞个截图?--因为本人前写过类似小网站,发现如果后台插件获取视频截图麻烦,但直接显示视频也就是video标签的话会出现当加载大量视频时,出现卡顿,导致部分视频无法显示缩略图和无法播放的现象。]
运行项目重新访问http://127.0.0.1:5000/ 查看效果
第四步 添加点击播放功能:
1.修改index.html为以下代码(请注意修改变化)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>小视频</title>
<script src="/static/js/jquery-2.1.4.min.js"></script>
<script src="/static/js/minigrid.js"></script>
<style>
/*全局样式*/
* {
margin: 0;
padding: 0;
}
a {
text-decoration: none;
}
body {
line-height: 1;
background: #FFF;
}
body {
font-family: "Microsoft Yahei";
background: #f4f4f4;
min-width: 1000px;
height: 100%;
}
/*主体居中*/
.container {
width: 98%;
position: relative;
margin-bottom: 20px;
margin: 0 auto;
}
#photo-box .photo {
display: block;
/*float: left;*/
width: 225px;
background-color: #fff;
overflow: hidden;
position: absolute;
-webkit-transition: .3s ease-in-out;
-o-transition: .3s ease-in-out;
transition: .3s ease-in-out;
border: 1px solid #e2e2e2;
}
#photo-box .photo .content {
margin: 0 10px;
font-size: 14px;
color: #777;
line-height: 20px;
word-break: break-all;
}
/*显示加载*/
#load-over {
width: 95%;
margin: 0 auto;
border-bottom: 1px #ccc solid;
border-top: 1px #ccc solid;
padding: 20px 0px;
text-align: center;
margin-bottom: 30px;
color: #999;
}
/*弹出播放页面样式*/
.mask {
z-index: 999;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(229, 229, 229, .95);
overflow: auto;
padding-top: 30px;
}
.mask .close-layer {
width: 56px;
height: 56px;
position: fixed;
right: 14px;
top: 0;
cursor: pointer;
z-index: 99995;
}
.mask .close-layer i {
background: url(/static/img/btn_close_layer.png) 0 0 no-repeat;
width: 36px;
height: 36px;
position: absolute;
left: 50%;
top: 50%;
margin: -18px 0 0 -18px;
}
.pin-view-wrapper {
width: 84%;
margin: 0 auto;
position: relative;
}
.pin-view-wrapper>.main-part,
.pin-view-wrapper>.side-part {
background-color: #fff;
display: block;
width: 100%;
}
.pin-view-wrapper>.main-part {}
.pin-view-wrapper>.side-part {
margin-bottom: 15px;
}
ul {
list-style: none outside none;
padding-left: 0;
margin: 0;
}
.main-part .item img {
/*width: 100%;*/
display: block;
margin: 0 auto;
max-width: 100%;
}
.main-part .item {}
.lSSlideOuter .lSPager.lSGallery img {
display: block;
height: 100%;
max-width: 100px;
width: 100%;
max-height: 100px;
}
.clearfix video {
width: 640px;
margin: 0 auto;
height: 480px;
display: block;
background-color: #000;
}
</style>
<script>
//--获取视频缩略图--
function vload(obj) {
$(obj).removeAttr("poster");
var vimg = $("<img/>",{width:"100%"})[0];
captureImage(obj, vimg);
$(obj).after(vimg);
obj.remove();
minigrid('#photo-box', '.photo');
};
var scale = 0.8; //缩放
function captureImage(video, output) { //截图
try {
var videocanvas = $("<canvas/>")[0];
videocanvas.width = video.videoWidth * scale;
videocanvas.height = video.videoHeight * scale;
videocanvas.getContext('2d').drawImage(video, 0, 0, videocanvas.width, videocanvas.height);
output.src = videocanvas.toDataURL("image/png");
delete videocanvas;
} catch(e) {
output.src = "/static/img/status.gif"; //--status.gif为加载动画
}
};
</script>
</head>
<body>
<!--"photo-box"为瀑布流 图片容器-->
<div id="photo-box" class="container">
<!--"photo"显示视频缩略图与图片信息-->
<a class="photo" target="_blank" href="#">
[站外图片上传中……(15)]
<div class="content">
欢迎来到XXX(公告或广告位)
</div>
</a>
</div>
<div id="load-over" style="">
加载中...
</div>
<!--遮罩层-->
<div id="vdo-mask" class="mask" hidden="hidden" style="display: none;">
<div class="close-layer" onclick="$(this).parent().fadeOut(300);$('#vdo-tBox').attr('src','');$('.side-part').html('')">
<i></i>
</div>
<div class="pin-view-wrapper">
<div class="side-part" style="text-align: center;padding: 10px 10px;font-size: 16px;font-weight: 600; color: #777;"></div>
<div class="main-part" style="padding: 10px 10px;">
<div class="item">
<div class="clearfix">
<video id="vdo-tBox" style="height: 600px; width: 100%;" controls="controls">
</video>
</div>
</div>
</div>
</div>
</div>
<script>
minigrid('#photo-box', '.photo', 20, null,
function() {
//更新后回调
});//瀑布流插件初始化
window.addEventListener('resize', function() {//窗口大小改变自动排版
minigrid('#photo-box', '.photo');
});
function b_load(page){
$("#load-over").text("加载...");
$.getJSON("/getvdojson?page=" + page, function(data) {
if(data.length > 0) {
for(let i in data) {
if(data[i]==null){
$("#load-over").text("加载完毕");
continue;
}
$("#photo-box").append(
$("<a/>", {
class: "photo",
href:"javascript:void(0)",
onclick:"$('#vdo-mask').show(100); $('#vdo-tBox').attr('src','"+data[i].url+"');$('.side-part').html('"+data[i].name+" <small>(发布:"+(data[i].mtime.split('.')[0]).replace("T"," ").replace("/","-")+")</small>')"
})
.append($("<video/>", { src: data[i].url , preload: "metadata", onloadeddata:'vload(this)',poster:"/static/img/status.gif",width: "100%"
}))
.append($("<div/>", { class: "content" }).html(data[i].name) )
.append($("<input/>",{type:"datetime-local",value:(data[i].mtime).split('.')[0],readonly:"readonly"
}).css({width:"100%",
border:"none",
color: "#777",
"padding-left": "10px",
"padding-bottom": "10px"
}))
);
}
minigrid('#photo-box', '.photo');
}
});
}
page = 0;
window.onscroll = function() {
// var scrolltop=document.documentElement.scrollTop||document.body.scrollTop;
var tops = $(document).scrollTop(); //获取滚动条的位置
var sctop = $(document).height() - $(window).height();
if(tops >= sctop) //成立说明滚动条已在最底部
{
page = page + 1; //传递页码
b_load(page);
}
};
//默认先加载
b_load(page);
page=page+1;
b_load(page);
//播放时阻止 滚动条 上下滚动 防止滚动加载
$.fn.scrollUnique = function() {
return $(this).each(function() {
var eventType = 'mousewheel';
if(document.mozHidden !== undefined) {
eventType = 'DOMMouseScroll';
}
$(this).on(eventType, function(event) {
// 一些数据
var scrollTop = this.scrollTop,
scrollHeight = this.scrollHeight,
height = this.clientHeight;
var delta = (event.originalEvent.wheelDelta) ? event.originalEvent.wheelDelta : -(event.originalEvent.detail || 0);
if((delta > 0 && scrollTop <= delta) || (delta < 0 && scrollHeight - height - scrollTop <= -1 * delta)) {
// IE浏览器下滚动会跨越边界直接影响父级滚动,因此,临界时候手动边界滚动定位
this.scrollTop = delta > 0 ? 0 : scrollHeight;
// 向上滚 || 向下滚
event.preventDefault();
}
});
});
};
$('.mask').scrollUnique();//事件绑定
</script>
</body>
</html>
--注意:视频的名字尽量不要出现特殊符号,比如:#--