Nodejs学习笔记(八)--- Node.js + Express 实现上传文件功能(felixge/node-formidable) - por...
前言
前面讲了一个构建网站的示例,这次在此基础上再说说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文件中构建表单并实现前端验证 (样式使用和 http://www.cnblogs.com/zhongweiv/p/nodejs_express_webapp.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.可以利于jquer.form.js去提交表单,post中用res.json方法返回值,优化体验(同样引用这个js也会增大页面体积)
....
总之是个小例子,大家将就着看看吧^_^!