• Nodejs学习笔记(八)—Node.js + Express 实现上传文件功能(felixge/node-formidable)


    前言

      前面讲了一个构建网站的示例,这次在此基础上再说说web的常规功能----文件上传,示例以一个上传图片的功能为例子

      上传功能命名用formidable实现,示例很简单!

      PS:最近比较忙,距上一次更新已经比较久了^_^!

    formidable简介

      nodejs原生实现上传还是比较麻烦,有兴趣的自已去参考一下网上有网友写的代码

      这里选择了formidable,也是github上同类功能模块人气比较高的

      https://github.com/felixge/node-formidable

      https://www.npmjs.org/package/formidable

      优点:上传速度比较可观,占用内存也比较低,简单易用...

    创建项目安装formidable

       1.创建项目sampleUpload

    cd 工作目录

    express -e sampleUpload

      2.修改package.json文件,添加formidable依赖项

    {
      "name": "application-name",
      "version": "0.0.1",
      "private": true,
      "scripts": {
        "start": "node ./bin/www"
      },
      "dependencies": {
        "express": "~4.0.0",
        "static-favicon": "~1.0.0",
        "morgan": "~1.0.0",
        "cookie-parser": "~1.0.1",
        "body-parser": "~1.0.0",
        "debug": "~0.7.4",
        "ejs": "~0.8.5",
        "formidable" : "latest"
      }
    }

      3.安装依赖项

    cd sampleUpload && npm install

      安装成功,一切准备就绪,开始完成这个功能!

    实现上传功能

      1.index.ejs文件中构建表单并实现前端验证(样式使用和https://www.cnblogs.com/flyingeagle/p/9192962.html一致)

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">       
        <title><%= title %></title>
      
          <link rel="stylesheet" type="text/css" href="/stylesheets/bootstrap.min.css" />
        <link rel="stylesheet" type="text/css" href="/stylesheets/signin.css" />
      </head>
    
      <body>
    
        <div id="container" class="container">
           <% if (locals.success) { %> 
              <div id="alt_sucess" class="alert alert-success"> 
                <%- success %> 
              </div> 
           <% } %> 
    
          <% if (locals.error) { %> 
            <div id="alt_warning" class="alert alert-warning"> 
              <%= error %> 
            </div> 
          <% } %> 
    
          <form class="form-signin" role="form" method="post" enctype='multipart/form-data'>
            <h2 class="form-signin-heading">上传文件</h2>
            <input id="fulAvatar" name="fulAvatar" type="file" class="form-control" />     
            <br/>
            <button id="btnSub" class="btn btn-lg btn-primary" type="submit">上 传</button>
          </form>
                
        </div> 
      </body>
    </html>
    
    <script src="/javascripts/jquery-1.11.1.min.js" type="text/javascript"></script>
    
    <script type="text/javascript">
         String.prototype.format = function (args) {
                var result = this;
                if (arguments.length > 0) {
                    if (arguments.length == 1 && typeof (args) == "object") {
                        for (var key in args) {
                            if (args[key] != undefined) {
                                var reg = new RegExp("({" + key + "})", "g");
                                result = result.replace(reg, args[key]);
                            }
                        }
                    }
                    else {
                        for (var i = 0; i < arguments.length; i++) {
                            if (arguments[i] != undefined) {
                                var reg = new RegExp("({)" + i + "(})", "g");
                                result = result.replace(reg, arguments[i]);
                            }
                        }
                    }
                }
                return result;
        }
    
        $(function(){
            $('#btnSub').on('click',function(){           
                var fulAvatarVal = $('#fulAvatar').val(),  
                    errorTip = '<div id="errorTip" class="alert alert-warning">{0}</div> ';  
    
                $("#errorTip,#alt_warning").remove();
                
                if(fulAvatarVal.length == 0)
                {
                    $("#container").prepend(errorTip.format('请选择要上传的文件'));                                
                    return false;
                }
    
               var extName = fulAvatarVal.substring(fulAvatarVal.lastIndexOf('.'),fulAvatarVal.length).toLowerCase();
    
                if(extName != '.png' && extName != '.jpg'){
                   $("#container").prepend(errorTip.format('只支持png和jpg格式图片'));                             
                   return false;                
                }
                
                return true;                
            })
        });
    
    </script>

      这里一定要注意表单的enctype属性,这个就不多作解释了,如果是初次接触,看看http://www.w3school.com.cn/tags/att_form_enctype.asp

      2.实现index.js中上传逻辑

    var express = require('express')
        router = express.Router(),    
        formidable = require('formidable'),
          fs = require('fs'),
          TITLE = 'formidable上传示例',
        AVATAR_UPLOAD_FOLDER = '/avatar/'
    
    /* GET home page. */
    router.get('/', function(req, res) {
      res.render('index', { title: TITLE });
    });
    
    router.post('/', function(req, res) {
    
      var form = new formidable.IncomingForm();   //创建上传表单
          form.encoding = 'utf-8';        //设置编辑
          form.uploadDir = 'public' + AVATAR_UPLOAD_FOLDER;     //设置上传目录
          form.keepExtensions = true;     //保留后缀
          form.maxFieldsSize = 2 * 1024 * 1024;   //文件大小
    
        form.parse(req, function(err, fields, files) {
    
            if (err) {
              res.locals.error = err;
              res.render('index', { title: TITLE });
              return;        
            }  
           
            var extName = '';  //后缀名
            switch (files.fulAvatar.type) {
                case 'image/pjpeg':
                    extName = 'jpg';
                    break;
                case 'image/jpeg':
                    extName = 'jpg';
                    break;         
                case 'image/png':
                    extName = 'png';
                    break;
                case 'image/x-png':
                    extName = 'png';
                    break;         
            }
    
            if(extName.length == 0){
                  res.locals.error = '只支持png和jpg格式图片';
                  res.render('index', { title: TITLE });
                  return;                   
            }
    
            var avatarName = Math.random() + '.' + extName;
            var newPath = form.uploadDir + avatarName;
    
            console.log(newPath);
            fs.renameSync(files.fulAvatar.path, newPath);  //重命名
        });
    
        res.locals.success = '上传成功';
        res.render('index', { title: TITLE });      
    });
    
    module.exports = router;

      注意:在public文件夹中创建avatar文件夹以供文件存放

    运行结果

      1.在app.js中添加8000端口的监听

      2.cd到项目根目录,运行node app

      3.浏览器上访问 localhost:8000

      

      4.上传图片

      
      5.去上传文件夹中查看

      

      大功告成!

    部分疑惑解析

      有一些在代码中有注释的我就不解释了

      1.怎么没看到上传?

      注意:form.parse  再看看formidable的解释 

    Automatically writing file uploads to disk

      2.为什么需要fs.renameSync

      原因和上一个问题有关,它会自动被上传到用户的临时目录(这个可以把files.fulAvatar.path输出来看一下便知) http://nodejs.org/docs/latest/api/fs.html#fs_fs_renamesync_oldpath_newpath

      3.可不可以看到上传进度

      可以,https://github.com/felixge/node-formidable#events (仔细看看events,可能有你要用的其它部分)

      但是只是在控制台输出,想在前端去显示进度条是不行的(没有去研究,想想别的办法应该也可以)

      4.文件名想命名UUID不重复,在nodejs中怎么办?

      可以使用node-uuid,调用简单  https://github.com/broofa/node-uuid

      ...

      其它的留言问吧^_^!

    写在之后

      功能比较简单,代码也是示例风格,大家主要关注一下使用

      代码结构优化方向:

      1.比如文件后缀这一类的方法可以放到一个pub.js中,此js专门用于这些公有方法

      2.可以利于返回值的方式去返回数据,前端根据返回值做出相应的提示

      3.可以利于jquery.form.js去提交表单,post中用res.json方法返回值,优化体验(同样引用这个js也会增大页面体积)

      ....

      总之是个小例子,大家将就着看看吧^_^!

    此系列的源代码可到http://bijian1013.iteye.com/blog/2425085下载。 

    文章来源:https://www.cnblogs.com/zhongweiv/p/nodejs_express_formidable.html

  • 相关阅读:
    命令行打开无线网络设置
    WebView2 的使用记录
    QT-Mac:在Mac下实现QT程序的打包及公证
    QT:CEF实现js函数与C++函数的异步调用
    QT-Mac:QT Creator 中QT Versions显示无效的QT版本的解决办法
    QT-Mac: Undefined symbols for architecture x86_64
    全局键盘钩子失效与WebRTC的关系分析
    第十二届蓝桥杯决赛 大学 B 组 C/C++ 做题记录
    Luogu P2671 求和 | 数学奇偶性&前缀和
    2021铁三决赛 PWN cardstore | 格式化字符串 & ret2libc
  • 原文地址:https://www.cnblogs.com/flyingeagle/p/9192994.html
Copyright © 2020-2023  润新知