node.js的express web框架非常好用,结合一些中间件能够实现很强大的功能。

而multer是express推荐使用的文件上传的中间件,本身也是非常的实用和方便。

一、目标

使用expree和multer实现单图片上传功能。

准备条件:

  • npm express multer body-parser --save

需要使用body-parser进行post表单解析,因此确保安装上述modules

二、html界面

创建一个简单的uploadFile.html,用于界面表单提交,name是image,type是file,method是post。

注意一定要使用enctype="multipart/form-data",否则multer是不会处理的。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>文件上传表单</title>
</head>
<body>
<h3>文件上传:</h3>
选择一个文件上传: <br />
<form action="./file_upload" method="post" enctype="multipart/form-data">
    <input type="file" name="image" size="50" />
    <br />
    <input type="submit" value="上传文件" />
</form>
</body>
</html>

三、upload_file.js

使用express框架实现web内容。

设定如下

一个问题

如下图所示,我的这个upload_file.js是在 test/express/目录,如果上传指定的目录是./public/upload/的话,会直接在test/下创建public/upload/文件夹,这样子就不是最终需要的目录。

postbird

一开始花了很久不知道为什么,后来发现,因为我在webstorm中设置了当前的project的工作目录如下图所示,根目录是test,才找到了原因.

postbird

不明白为什么multer({dest:'./public/'})中,不是相对路径,而实际上是使用的绝对路径,也许是为了项目复杂之后的考虑吧。

思路如下:

  1. 用户请求 127.0.0.1,显示uoloadFile.html
  2. 用户选择图片并进行表单提交
  3. upload_file.js接收post并进行上传,上传过程中会产生一个临时文件,然后使用filesystem的readFile去读取这个临时文件并再次将读取的data通过filesystem的writeFile写入到路径中,然后再使用fileSystem的unlink删除临时文件即可完成,然后在进行reponse的输出。

其中上面第三点是精华所在。

四、代码

upload_file.js代码如下

/**
 * Created by Postbird on 2017/2/22.
 */
var express=require('express');
var fs=require('fs');
var bodyParser=require('body-parser');
var multer=require('multer');
var path=require('path');

//创建爱app
var app=express();
app.use(bodyParser.urlencoded({extended:false}));

//设置文件上传的public/upload路径
var uploadDir='./express/public/upload/';
//规定只上传一张图片 使用single
var upload=multer({dest:uploadDir}).single('image');

app.get('/',function (req,res){
    res.sendFile(__dirname+'/'+'uploadFile.html');
});

app.post('/file_upload',function(req,res,next){
    //文件上传
    upload(req, res, function(err){
        if(err){
            console.error(err.message);
        }else{
            //获取文件的名称,然后拼接成将来要存储的文件路径
            var des_file=uploadDir+req.file.originalname;
            //读取临时文件
            fs.readFile(req.file.path,function(err,data){
                //将data写入文件中,写一个新的文件
                fs.writeFile(des_file,data,function(err){
                    if(err){
                        console.error(err.message);
                    }else{
                        var reponse={
                            message:'File uploaded successfully',
                            filename:req.file.originalname
                        };
                        //删除临时文件
                        fs.unlink(req.file.path,function(err){
                            if(err){
                                console.error(err.message);
                            }else{
                                console.log('delete '+req.file.path+' successfully!');
                            }
                        });
                    }
                    res.end(JSON.stringify(reponse));
                });

            });
        }
    });

});

var server=app.listen(80,function(){
    var host=server.address().address;
    var port=server.address().port;

    console.log('Server at http://%s:%s',host,port);
});

五、多文件上传

单文件上传和多文件上传的区别主要如下:

single 、array、fields的区别-https://github.com/expressjs/multer/blob/master/doc/README-zh-cn.md#singlefieldname

多文件上传 js代码

下面代码中其实最大的区别就是使用了multer.array(),获取文件列表的时候使用的是req.files,将req.file进行forEach就可以,不建议使用for循环来处理,因为是非阻塞式。使用for会出现无法写入读取的问题。

/**
 * Created by Postbird on 2017/2/22.
 */
var express=require('express');
var fs=require('fs');
var bodyParser=require('body-parser');
var multer=require('multer');

//创建app
var app=express();
app.use(bodyParser.urlencoded({extended:false}));

//设置文件上传存储路径
var uploadDir=__dirname+'/public/upload/';
//设置multer upload
var upload=multer({dest:uploadDir}).array('images');

// 请求 / 返回 uploadMoreFile.html
app.get('/',function(req,res){
    res.sendFile(__dirname+'/'+'uploadMoreFile.html');
});

//post请求 提交表单
app.post('/file_upload',function(req,res,next){
    //多个文件上传
    upload(req,res,function(err){
        if(err){
            console.error('[System] '+err.message);
        }else{
            //循环处理
            var fileCount=req.files.length;
             req.files.forEach(function(i){
                 //设置存储的文件路径
                 var uploadFilePath=uploadDir+i.originalname;
                 //获取临时文件的存储路径
                 var uploadTmpPath=i.path;
                 //读取临时文件
                 fs.readFile(uploadTmpPath,function(err,data){
                     if(err){
                         console.error('[System] '+err.message);
                     }else{
                         //读取成功将内容写入到上传的路径中,文件名为上面构造的文件名
                         fs.writeFile(uploadFilePath,data,function(err){
                             if(err){
                                 console.error('[System] '+err.message);
                             }else{
                                 //写入成功,删除临时文件
                                 fs.unlink(uploadTmpPath,function(err){
                                     if(err){
                                         console.error('[System] '+err.message);
                                     }else{
                                         console.log('[System] '+'Delete '+uploadTmpPath+' successfully!');
                                     }
                                 });
                             }
                         });
                     }
                 });
            });

            //所有文件上传成功
            //回复信息
            var reponse={
                message:'File uploaded successfully',
            };
            //返回
            res.end(JSON.stringify(reponse));
        }
    });
});

//监听端口
var server=app.listen(80,function(){
    var host=server.address().address;
    var port=server.address().port;
    console.log('[System] Listen at http://%s:%s',host,port);
});

postbird