Better

Ethan的博客,欢迎访问交流

文件拖拽上传

博客使用的过程中,发现有时候需要添加图片,发现简书是可以拖拽上传的,因此琢磨这给自己的博客也加上这个功能。

__dirname & __filename

  • __dirname:获得当前文件所在目录的完整目录名
  • __filename:获取当前模块文件的带有完整绝对路径的文件名

path模块

  • 格式化路径:path.normalize(p)
  • 路径合并:path.join([path1], [path2], […])
  • 返回路径的所在的文件夹名称:path.dirname(p)。p传"",可得到应用根路径。
  • 返回指定的文件名,返回结果可排除[ext]后缀字符串:path.basename(p, [ext])
  • 返回指定文件名的扩展名称:path.extname(p),带有.
  • 特定平台的文件分隔符 path.sep

fs模块

这个东西很强大,可以当成Java的IO看待。

  • 读文件:readFile(filename,[options],callback);
  • 写文件:writeFile(filename,data,[options],callback);
    • filename, 必选参数,文件名
    • data, 写入的数据,可以字符或一个Buffer对象
    • [options],flag,mode(权限),encoding
    • callback 读取文件后的回调函数,参数默认第一个err,第二个data 数据
  • 追加文件:appendFile(filename,data,[options],callback);
  • 创建目录:mkdir(path, [mode], callback);
  • 读取目录:readdir(path, callback);
  • 文件与目录的是否存在:exists(path, callback);
  • 移动/重命名文件或目录:rename(oldPath, newPath, callback);
  • 删除空目录:rmdir(path, callback);
  • 文件流
    function(index){
      var filename = req.files["file" + index].originalFilename || path.basename(req.files["file" + index].path);
      //path接口可以指定文件的路径和文件名称,"\结尾默认为路径,字符串结尾默认为文件名"
      var targetPath = path.dirname("") + '/public/autoupload/' + filename;
      //fs创建指定路径的文件并将读取到的文件内容写入
      fs.createReadStream(req.files["file" + index].path).pipe(fs.createWriteStream(targetPath));
    }
    

connect-multiparty

专门用来处理文件上传的中间件,通过如下方式声明,然后再需要上传文件的接口注入。

var multipart = require('connect-multiparty');
var multipartMiddleware = multipart(option);//option可以传入uploadDir参数设置上传的地址
app.post('/formdata',multipartMiddleware, function (req,res) {
res.send(req.body,req.files,req.files.file.path);
//分别返回body,文件属性,以及文件存放地址
});

在官方文档中,在请求内有这么一句注释don't forget to delete all req.files when done。看大家貌似都是通过connect-multiparty上传到临时文件夹(不晓得为什么),然后通过fs模块将文件移动到指定位置,最后删除。

var source = fs.createReadStream(path);
var dest = fs.createWriteStream(output);
source.pipe(dest);
source.on('end', function() { fs.unlinkSync(path);});   //delete
source.on('error', function(err) {  });

express-formidable

接收表单及文件的上传中间件

  • 在应用级别使用,此时针对所有请求,只要是有file字段的表单类型,或者是FormData的ajax类型,都会将文件上传到服务器。代码如下:
    app.use(require('express-formidable')({
      uploadDir: path.join(__dirname, 'public/img'),// 上传文件目录
      keepExtensions: true// 保留后缀
    }));
    
    这样一来,中间件formidable处理完成之后通过req.files.xxx得到的对象是经过加工的,如果选择了文件,那么req.files.xxx.name值为原始文件名称,否则为空。req.files.xxx.path得到文件的上传物理路径,物理路径在网站中直接使用会找不到,在代码中要转成相对站点的虚拟路径,path的文件名称是经过加工保证唯一的。
  • 接口级别使用,因为应用级别不能针对接口特定控制
    var formidable = require('express-formidable')({
      uploadDir: path.join(path.dirname(""), 'public/img'),// 上传文件目录
      keepExtensions: true// 保留后缀
    });
    router.post('/', checkNotLogin, formidable, function (req, res, next) {});
    
    很可悲,我取消express-formidable在全局app中注册后,其余接口都报错了,提示req.fields等于undefined,而且我的form表单并没有添加enctype=multipart/form-data,可见express-formidable所做的工作不仅仅针对file类型,源于无知啊,我天真的以为req.fileds是基本api。 你肯定很好奇,我开始为什么不愿意将express-formidable在应用级别注册,还是那句话,一切源于无知啊,因为我一开始不会使用这个实现拖拽上传,而是使用了connect-multiparty的方式,然后这两个就会打架咯,connect-multiparty提示流已经被关闭,理解了原理之后,我发现express-formidable贼好用,不多BB了,越BB越无知。

获取参数的方法

哪些是原始字段呢?看这个问题,也许觉得很奇怪,因为我想弄清楚,哪些数据是可以直接通过req的属性得到,哪些需要使用中间件进行加工才可以,比如上述req.fileds和req.file是依赖`express-formidable中间件的。

  • req.params.xxx得到路径参数
  • req.query.xxx得到查询参数
  • req.body.xxx得到请求体参数(依赖body-parser?)

Ajax奇怪问题

  • Ajax相对路径
    • 本想项目尽量使用相对路径,这样不用针对线上环境和开发环境进行区分。但是不知道为何使用相对路径总是提交到当前页面。
    • 问题总算是找出来,开发中没有注意,在请求上添加了一个checkLogin的中间件,在中间件的处理中会有res重定向,由于是ajax请求的关系,ajax请求不能重定向,因此重定向并没有生效,反而导致请求总是提交到当前页面,这里也不知道是为何?
    • supervisor这个工具也是有点坑啊,突然之间不生效了,害我左改又改,怎么都找不出这个bug了,后来他发现他根本就没有重启应用了,所以开发中也要注意下,代码改动后,应用有没有自动重启。

测试

540da27c6b53d5c0d955ff928145b14b.jpg



留言