美文网首页Node学习之路
Node.js学习第四、五天笔记之myAlbums项目实战

Node.js学习第四、五天笔记之myAlbums项目实战

作者: 果木山 | 来源:发表于2018-12-10 23:22 被阅读0次

    MVC

    • 指的是:models views controller;
      • models:用于后台数据的查找;
      • views:视图,用于存储浏览器页面的文件;
      • controller:管理者,控制者;用于二者之间的控制;

    myAlbum项目开发

    • 创建文件夹:命令:mkdir
      • models:提供数据;
      • views:模板文件;
      • controller:控制器;
      • uploads:上传的文件
      • public:静态资源
    • 创建文件:命令:touch ; window命令行中使用type nul>
      • app.js:入口文件,创建服务器;
    • package.json文件:记录当前项目依赖;命令npm init -y;
    • 安装项目模块框架:命令npm install --save-dev express ejs formidable
    • controller文件夹下创建router.js文件;作为一个路由的控制器;
      • 知识点:在app.js中引入自定义模块,必须添加相对路径;如:const router=require("./controller/router");
      • 在router.js中设置代码输出参数;
       exports.showIndex=function (req, res) {
           res.render("index")
       };
      
      • 在app.js中直接引入:router.showIndex获取匿名函数;
    • 静态资源文件加载
      • 设置静态资源在public文件下:app.use(express.static("./public"));;即相对路径都在public目录下;
      • 在ejs文件中,通过link引入css文件和通过script引入js文件;都会发送对应的请求;请求地址为href和src中的路径地址;
      • 在通过get请求,打开index.ejs文件后,会在页面中继续发送请求;页面想要有css样式和js动效,必须保证css文件和js文件能够正常引入;如何在页面中渲染打开css文件;css的引入地址是很重要的;
      • 代码:app.use(express.static("./public"));中将静态资源的目录设置在public目录下,即所引入的css文件和js文件必须在此目录下添加相对路径,然后找到文件才能打开;
      • 所以,将css文件和js文件放在public文件夹下,在引入时添加相对路径;如:<link href="/css/bootstrap.css" rel="stylesheet"><script src="/js/bootstrap.js"></script>;注意:css前斜杠最好添加上,若不添加,会被拼接在地址的后面,会出错,所以必须添加;
      • 在地址栏中输入地址localhost:8080/css/bootstrap.css也可以打开引入的css文件;
    • 静态资源设置的含义
      • 代码:app.use(express.static("./public"))
      • 含义:在express中,设置静态资源的请求路径为public目录下;
      • 验证:在地址栏中输入localhost:8080/index.html地址,指的含义是查找到public目录下的index.html,打开并渲染到页面上;
      • 在index.html文件中有link引入的css文件;如:<link href="/css/bootstrap.css">;其中link的href会发送请求;相当于在地址栏中输入localhost:8080/css/bootstrap.css地址;即,在public目录下查找css文件夹下的bootstrap.css文件,打开并渲染到页面中;
      • 结论:
        • 设置了静态资源的路径;则在地址栏中寻找的文件都是在public目录下查找,如果查找到,则渲染,查找不到,则会查看其它的请求是否存在此地址;
        • 打开html或ejs文件中,通过link或script或img中的href或src等引入文件,都会向服务器发送请求;会默认在public目录下查找;
    • 简单的实现index.ejs文件的页面打开渲染:
      • index.ejs文件,通过设置get请求:app.get("/",function(req,res){ res.render("index")};);即:在地址栏中输入"/",后可执行匿名函数;在匿名函数中通过res.render来默认打开views目录下的index.ejs文件;
      • 打开index.ejs文件后,里面引入的href和src再次发送多个请求;在设置的静态资源目录public文件夹下查找文件;然后打开渲染;
      • 代码:
        • index.ejs文件代码:
         <!DOCTYPE html>
         <html lang="zh-CN">
         <head>
             <meta charset="utf-8">
             <title>模板</title>
             <link href="css/bootstrap.css" rel="stylesheet">
         </head>
         <body>
         <h1>你好,世界!</h1>
         <script src="js/jquery.js"></script>
         <script src="js/bootstrap.js"></script>
         </body>
         </html>
        
        • app.js服务器代码:
         const express=require("express");
         const app=express();
         const router=require("./controller/router");//引入自定义模块,必须添加相对路径;
         app.listen(8080);
         
         //发送请求,静态渲染页面
         app.use(express.static("./public"));
         //设置模板引擎
         app.set("view engine","ejs");
         //通过"/"来发送请求,打开views下的index.ejs文件
         app.get("/",router.showIndex);
        
        • router.js管理器代码:
         exports.showIndex=function (req, res) {
             res.render("index")
         };
        

    项目开发

    • 搭建静态页面index.ejs;利用bootstrap模板来创建页面布局;
      • 页面中相册的个数取决于uploads目录下文件夹的个数;
      • 在页面中利用ejs模板创建for循环,在服务器中通过render()传送参数;即在router.js管理器中获取参数;router.js再去models中获取数据;然后最终传送给views中ejs页面;
    • 获取磁盘中uploads目录下文件夹的个数;
      • 在models中files.js中获取数据,导出数据;利用fs文件系统中的readdir()来获取文件夹下所有的文件,再通过fs.stat()获取每个文件的状态stats,判断stats.isDirectory()是否为true;如果文件是文件夹,则返回true;
      • 通过自执行函数和递归函数,将异步强制变成同步;然后待遍历完,输出数组;
      • 在匿名函数中如何输出文件:通过callback参数;注意传出的数据包括err错误信息;
      • 在router.js中引入自定义模块files.js,添加相对路径;通过匿名函数中的参数拿到文件夹信息;然后通过res.render()传送给ejs文件使用;
      • 注意点:
        • 在使用fs.readdir(path,()=>{})时,path路径为"./uploads",其中"./"拿到的是根目录;不是"../"拿到根目录;
        • 数据的输出通过回调函数,将异步操作中的结果输出,但要注意输出不同时刻的err信息;在成功输出数据时,赋值为null,这样在if(err)条件语句中,就不会走语句;
      • 代码:
        • ejs代码:
         <div class="container">
             <div class="row">
                 <% for(var i=0; i<albums.length; i++){%>
                     <div class="col-xs-6 col-md-3">
                         <a href="/" class="thumbnail">
                             <img src="/img/01.jpg" alt="文件夹图片">
                         </a>
                         <h5 style="text-align: center;"><%= albums[i]%></h5>
                     </div>
                 <% }%>
             </div>
         </div>
        
        • app.js代码:app.get("/",router.showIndex);
        • router.js代码:
         //引入models中的files.js模块;
         const modFiles=require("../models/files");
         //显示首页
         exports.showIndex=function (req, res) {
             modFiles.showAllAlbums(function(err,albums){
                 if(err){
                     res.send(err);
                     return;//阻断程序执行;
                 }
                 res.render("index",{
                     albums
                 })
             });
         };
        
        • files.js代码:
         //用于生产数据,然后输给router.js使用;
         const fs=require("fs");
         exports.showAllAlbums=function (callback) {//数据以回调函数的形式输出;
             fs.readdir("./uploads",(err,files)=>{//地址为根目录下的地址;
                 if(err){
                     callback(err,null);
                     return;
                 }
                 var ary=[];
                 //把异步操作强制变成了同步操作;
                 (function interator(i){
                     if(i>=files.length){
                         callback(null,ary);//null代表不会走if(err)条件;
                         return;//阻断程序执行;
                     }
                     fs.stat("./uploads/"+files[i],(err,stats)=>{
                         if(err){
                             callback(err,null);
                             return;
                         }
                         if(stats.isDirectory()){
                             ary.push(files[i]);
                         }
                         interator(++i);
                     })
                 })(0);
             });
         };
        
    • 点击文件夹后发送请求,打开文件夹下的所有文件;
      • 发送请求:在地址栏中输入文件夹名字后,即发送了get请求,使其获的数据并打开渲染;在index.ejs中每一个文件夹都在一个a标签中,设置href为<a href="/<%= albums[i]%>" class="thumbnail">,即根据文件夹的名字不同,发送不同的请求;跟地址栏中输入localhost:8080/大学是一样的效果;从而实现页面的跳转;
      • 打开页面;需要新建一个showImg.ejs文件,里面是新的内容;在发送/:albumsImg请求后,在files.js中打开该文件夹,获取文件夹下所有的文件,通过fs.isFile()筛选出不是文件夹的文件;通过匿名函数传出;然后渲染到页面上,展示出相应个数和名字的图集;
      • 在页面渲染中打开img图片;在files.js中只能拿到文件夹下的文件个数和文件名称;无法打开渲染;res.render的作用是,打开showImg.ejs文件,并且给其传数据;只能渲染页面,不能打开img图片;
      • 呈现img图片在页面中,需要img标签中的src再次发送强求,服务器接受进行响应;所以,需要再设置静态资源到./uploads目录下,指的是,发送来的请求闲杂public目录下找文件,如果找不到,就到uploads目录下查找,找到文件,渲染打开文件;
      • 注意1:img中src的路径需要注意;当showImg.ejs被渲染到页面上的时候,src才开始发送请求;包括css和js也会发送请求;
        • img的src设置时有两种方式;1)<img src="/<%= albumsImg%>/<%= albums[i]%>" alt="文件夹图片">,在地址中添加文件夹路径;2)<img src="<%= albums[i]%>" alt="文件夹图片">,在地址中不添加文件夹路径但是不能添加"/",可能的原因是在地址栏中已经存在http://localhost:8080/大学/;
        • css和js文件src中必须添加"/",不能省略;
      • 注意2:使用req.params.albumsImg获取请求地址时,除了文件夹地址,还会得到favicon.ico,所以需要在获取文件夹下文件的函数中,阻止favicon.ico运行;
      • 代码:
        • showImg.ejs代码:
         <!DOCTYPE html>
         <html lang="zh-CN">
         <head>
             <meta charset="utf-8">
             <title>展示图片</title>
             <link href="/css/bootstrap.css" rel="stylesheet">
         </head>
         <body>
         <% include include/header.ejs%>
         <section class="container">
             <ol class="breadcrumb">
                 <li><a href="/">全部相册</a></li>
                 <li class="active"><%= albumsImg%></li>
             </ol>
         </section>
         <div class="container">
             <div class="row">
                 <% for(var i=0; i<albums.length; i++){%>
                     <div class="col-xs-6 col-md-3">
                         <span class="thumbnail">
                             <img src="/<%= albumsImg%>/<%= albums[i]%>" alt="文件夹图片">
                         </span>
                         <h5 style="text-align: center;"><%= albumsName[i]%></h5>
                     </div>
                 <% }%>
             </div>
         </div>
         <script src="/js/jquery.js"></script>
         <script src="/js/bootstrap.js"></script>
         </body>
         </html>
        
        • app.js代码:
         const express=require("express");
         const app=express();
         const router=require("./controller");//引入自定义模块,必须添加相对路径;因为controller创建了package.json文件,在里面设置了main属性,设置默认的入口文件为router.js文件;
         app.listen(8080);
         
         //发送请求,静态渲染页面
         //1.设置静态资源路径在public目录下,用于渲染引入的css文件和js文件;
         app.use(express.static("./public"));
         //2.设置静态资源路径在uploads目录下,用于渲染img图片;
         app.use(express.static("./uploads"));
         
         //设置模板引擎
         app.set("view engine","ejs");
         //通过"/"来发送请求,打开views下的index.ejs文件
         app.get("/",router.showIndex);
         //发送get请求:地址:"/大学",打开views下的showImg.ejs文件;
         app.get("/:albumsImg",router.showImg);
        
        • router.js代码:
         //显示相册文件夹中的图片
         exports.showImg=function (req, res) {
             var albumsImg=req.params.albumsImg;
             modFiles.showAlbumsImgs(albumsImg,function (err,albums,albumsName) {
                 //如果有错误,则显示在页面上,并阻断程序执行;
                 if(err){
                     res.send(err);//send后面可以写return;
                     return;//必须阻断程序执行;
                 }
                 res.render("showImg",{
                     albumsImg,
                     albums,
                     albumsName
                 })
             });
         };
        
        • files.js代码:
         //获取地址栏中发送的地址文件夹下的所有图片文件;生成数组导出;
         exports.showAlbumsImgs=function (albumsImg, callback) {
             //albumsImg是地址栏中的文件夹名称;callback用来导出数据;
             //读取文件夹下的文件
             fs.readdir("./uploads/"+albumsImg,function (err, files) {
                 if(err){
                     callback("读取文件夹失败",null,null);
                     return;
                 }
                 var albums=[];
                 var albumsName=[];
                 (function interator(i) {
                     if(i>=files.length){
                         callback(null,albums,albumsName);
                         return;
                     }
                     fs.stat("./uploads/"+albumsImg+"/"+files[i],function (err, stats) {
                         if(err){
                             callback("读取文件夹下的文件失败",null,null);
                             return;
                         }
                         if(stats.isFile()){
                             //成立,则证明不是文件夹
                             albums.push(files[i]);
                             albumsName.push(path.parse(files[i]).name);
                         }
                         interator(++i);
                     })
                 })(0);
             })
         };
        
    • 页面的操作:
      • 提炼出两个ejs页面中的公共的代码,放在views下的include文件夹下的header.ejs文件中;这样在文件中引入此文件即可;
      • 优点:当页面公共部分的代码,发生变化,只需在header.ejs文件中修改即可,否则,还需分别再两个文件中修改;
      • 引入include.ejs文件代码:<% include include/header.ejs%>,其中include/header.ejs为相对路径,无需加引号;
    • 图片上传功能
      • 思路:
        • 创建form.ejs表单页面;通过form表单来提交数据;
        • 发送get请求,通过header.ejs中的上传a标签href设置地址,点击发送请求;打开form.ejs页面;传送数据;用于select表单中获取文件夹数据;
        • 发送post请求,通过表单中submit提交按钮,设置form中的action和method;点击发送post请求;
        • 在router.js中通过formidable模块来获取上传的数据;
          • 新建form对象;var form= new formidable.IncomingForm()
          • 设置上传地址;form.uploadDir="./uploads/"
          • 通过form.parse()拿数据;=》fields:表单上传参数组成的对象;files:表单file中name组成的对象;用于获取上传图片的路径名字;以及上传之前的名字;
          • 通过files.tupian.size,拿到上传文件的大小,然后进行判断,如果大于某值,则用fs.unlink()删除已经上传的文件;注意,在条件判断中设置return;阻断程序向下运行;
          • 通过fs.rename()将oldpath改为newpath,修改原来文件名字并移动该文件到新的路径下;
        • 修改成功后,打开对应文件夹的图集;调用modFiles.showAlbumsImgs(albumsImg,function (err,albums,albumsName){})
        • 修改成功后,可以通过 res.redirect("/中学")来进行重定向页面跳转;
      • 注意点:
        • 设置get请求"/upload"时,必须放在app.get("/:albumsImg",router.showImg);前面,否则,不会走该请求;必须将严格的请求放在不严格的请求的前面;
        • form表单中,seclect和file必须设置name值,否则无法获取数据;seclect的name值用来获取放在哪个文件夹下,file的name值是用来作为files对象中属性名来获取上传文件的数据的;
        • form表单中的action设置请求地址,method设置post请求,enctype设置大文件下的类型;
        • 利用formidable模块获取files对象后,path属性拿到的是路径加文件名;作为oldpath;newpath包含相对路径和文件名;相对路径设置为uploads下的对应文件夹中;
        • 在修改名字后,原来上传的文件会被移动到新的路径下;
        • 在设置上传地址时地址后面必须加"/",否则,无法使用;如form.uploadDir="./uploads/";
        • 注意异步和同步问题;
      • 代码:
        • form-upload.ejs代码:
         <!DOCTYPE html>
         <html lang="zh-CN">
         <head>
             <meta charset="utf-8">
             <title>表单上传文件</title>
             <link href="/css/bootstrap.css" rel="stylesheet">
         </head>
         <body>
         <% include include/header.ejs%>
         <section class="container">
             <form action="/doupload" method="post" enctype="multipart/form-data">
                 <div class="form-group">
                     <label for="wenjian">相册目录</label>
                     <select class="form-control" name="wenjianjia" id="wenjian">
                         <% for(var i=0; i<albums.length; i++){%>
                             <option><%= albums[i]%></option>
                         <%}%>
                     </select>
                 </div>
                 <div class="form-group">
                     <label for="InputFile">上传文件路径</label>
                     <input type="file" id="InputFile" name="tupian">
                 </div>
                 <button type="submit" class="btn btn-primary">上传</button>
             </form>
         </section>
         <script src="/js/jquery.js"></script>
         <script src="/js/bootstrap.js"></script>
         </body>
         </html>
        
        • app.js代码:
         /发送get请求:地址:"/upload",打开views下的form.ejs文件;
         app.get("/upload",router.upload);
         //发送post请求,地址:"/doupload",上传文件;
         app.post("/doupload",router.doUpload);
         //以上代码必须放在下面代码前面;
         //发送get请求:地址:"/大学",打开views下的showImg.ejs文件;
         app.get("/:albumsImg",router.showImg);
        
        • router.js代码:
         //上传文件获取数据
         exports.doUpload=function (req, res) {
             //新建一个form对象
             var form= new formidable.IncomingForm();
             //设置上传地址
             form.uploadDir="./uploads/";
             //解析文件
             form.parse(req,(err,fields,files)=>{
                 //修改文件名,放在指定文件夹下
                 var oldpath=files.tupian.path;
                 var newpath=form.uploadDir+fields.wenjianjia+"/"+files.tupian.name;
                 fs.rename(oldpath,newpath,function (err) {
                     if(err){
                         res.send("修改名字失败");
                         return;
                     }else{
                         //res.send("上传成功");//注意异步问题;所以此代码不能写在外面;
                         //当上传成功后,跳转到相应的文件夹中打开;
                         var albumsImg=fields.wenjianjia;
                         modFiles.showAlbumsImgs(albumsImg,function (err,albums,albumsName) {
                             //如果有错误,则显示在页面上,并阻断程序执行;
                             if(err){
                                 res.send(err);//send后面可以写return;
                                 return;//必须阻断程序执行;
                             }
                             res.render("showImg",{
                                 albumsImg,
                                 albums,
                                 albumsName
                             })
                         });
                     }
                 })
             });
         };
        
    • 接盘侠
      • 在最后设置use请求,请求地址为"/",即所有的地址都可以进入请求;渲染打开404.ejs文件,显示404页面;
      • 代码:
       //设置404页面,用来接底
       app.use("/",function (req, res) {
           res.render("404");//打开404.ejs文件;
       });
      
      • 利用middleware中间件来设置错误流,当进入一个请求后,如果出现err,则通过next(),来向下接着请求,最终到达use请求,渲染404页面;
      • 在router.js文件中所有请求的匿名函数;均要设置next,当err情况下,设置next(),并阻断程序执行;
      • files.js文件中err不需要设置next(),因为通过callback已经将err返回到router.js中;
      • 这样设置,也可以解决地址具体地址和广泛地址的影响;
      • 注意:send()后面不能设置任何东西;可以写return;

    项目开发中的架构格局

    • 文件分类及作用
      • MVC结构:
        • M:models:用来获取后台的数据库,通过exports输出给controller管理器;
          • 创建files.js文件,作为后台数据的模块;通过exports来传出数据,在router.js管理器中引入此模块,用于获取传出数据;
        • V:views: 目录下存在ejs文件,作为浏览器视图的渲染文件;
          • 存储ejs文件,用于页面的打开渲染;
          • 创建include文件夹,里面创建header.ejs文件,作为ejs文件的公共部分代码;通过<% include include/header.ejs%>引入到其他文件中;
        • C:controller:管理器;用来连接models和views;
          • 作用:作为一个管理者,在views中通过render来打开ejs文件,在数据库或models中拿数据;
          • 创建router.js文件,作为数据的管理器;通过exports来传出数据;在app.js中引入此模块来获取数据;
          • 创建package.json文件,通过设置里面的main属性,来设置默认入口文件router.js;则在app.js服务器文件中引入模块时,可以直接引入controller文件夹,如const router=require("./controller");
        • 三者之间的关系:
          • 当服务器中接受到浏览器发送的请求后,向controller管理器请求;如代码:app.get("/",router.showIndex);;router.js中设置匿名函数;渲染页面;
          • 当浏览器中需要uploads文件夹下的文件夹个数时,向controller管理器要数据;controller再向models后台数据库中查找获取;models数据也通过exports输出数据;
      • public文件夹:用于存储静态文件;如css,js文件;通过在服务器中设置静态渲染来引用文件;
      • app.js:为入口文件,用于创建服务器来前后台交互;
      • package.json:用于记录项目依赖的文件;
      • readme.md:读者读的文档,用于解读项目;
    • 请求及目的
      • get请求,是为了渲染打开页面;
        • get-"/"=>打开index.ejs;即,打开图片首页,根据uploads下文件夹的个数,呈现出相应个数的图集;
        • get-"/:albumsImg" => 打开showImg.ejs;即呈现出每个albumsImg文件夹下的图片;
        • get-"/upload" => 打开form上传文件表单;用于上传图片文件;
      • post请求,是真正做一些功能的,如上传文件;
        • post-"/doupload" => 通过表单中的submit提交按钮,来发送post请求,进行图片文件的上传;
      • use请求:请求地址设置为"/",可以省略,作为接盘侠;
        • use-"/" => 打开404.ejs文件,呈现404页面,配合middleware中间件使用;

    项目开发中的知识点

    • 在controller文件夹中创建package.json文件,里面设置main属性,来设置默认的入口文件为router.js,这样在app.js服务器中引入模块时,只引入文件夹controller;即:const router=require("./controller");
    • 在项目开发中如果匿名函数中存在err错误代码,必须设置相应的条件语句,然后传出错误信息;后面一定要加return,阻断程序执行;
    • 使用req.params.albumsImg获取请求地址时,除了文件夹地址,还会得到favicon.ico,所以需要在获取文件夹下文件的函数中,阻止favicon.ico运行;
    • 1M等于1024K,1K等于1024字节,文件上传时,通过files.tupian.size来获取上传文件的大小,单位为字节;
    • 接盘use请求和middleware的设置
      • use请求地址为"/",可以省略;当所有请求都不成立的时候,最后请求use的匿名函数,打开404页面文件;
      • 前提是在每个请求的匿名函数中设置middleware中间件;即next();当err时设置next(),并阻断程序执行;

    相关文章

      网友评论

        本文标题:Node.js学习第四、五天笔记之myAlbums项目实战

        本文链接:https://www.haomeiwen.com/subject/quyjhqtx.html