Nodejs学习笔记(5) 文件上传系统实例



 MySQL可以存放几乎任何类型的数据(图片、文档、压缩包等),但这不是最好的解决方案,正常情况下都是在数据库中存放文件路径,图片、音乐、视频、压缩包、文档等文件存放在硬盘上。


2018.8.4更新:

 在传输文件时,multer().array()设置的字段属性应该和前端中的<input>标签的name属性一致,否则会产生错误:“unexpected field: ......


1. 代码

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Upload File</title>
</head>
<body>
    <h2>Select a File</h2>
    <form action="/upload_file" method="POST" enctype="multipart/form-data">
        <input type="file" name="file"><br>
        <input type="submit" value="Submit">
    </form>

    <form action="/delete_all_file" method="GET">
        <input type="submit" value="删除所有文件">
    </form>

    <form action="/delete_cache" method="GET">
        <input type="submit" value="清除缓存">
    </form>
</body>
</html>

server.js

/**
 * 创建服务器
 * - 实现文件传输并合理存储
 * - 实现磁盘文件和数据库文件信息的同步一体
 * - 接收文件完毕后自动删除缓存
 * - 可以在前端删除所有文件(同步修改数据库)
 */

/********************************
************ 预定义 **************
*********************************/
//依赖
var express = require('express');
var app = express();
var mysql = require('mysql');
var fs = require('fs');

var bodyParser = require('body-parser');
var multer = require('multer');

var clear = require('./clear');
var db = require('./db');

// 中间件
app.use(bodyParser.urlencoded({ extended: false })); // 只解析请求中的字符串和数组(本例暂时没用到)

// 设置文件缓存的目录
var upload = multer({ dest: './uploadFiles/tmp/'});

/********************************
 ********************************
*********************************/

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

// 响应文件上传请求
app.post('/upload_file', upload.array('file'), function (req, res) {
    // 文件信息
    if (req.files[0]){
        console.log("----------接收文件----------\n");
        console.log(req.files[0]);
    }
    // 存储
    var des_file = __dirname + "/uploadFiles/" + req.files[0].fieldname + "/" + req.files[0].originalname;
    fs.readFile(req.files[0].path, function (error, data) {
        if (error) {
            return console.error(error);
        }
        fs.writeFile(des_file, data, function (err) {
            if (err) {
                // 接收失败
                console.log(err);
            }else {
                // 接收成功
                var response = {
                    message: 'File uploaded successfully!',
                    filename: req.files[0].originalname
                };
                console.log('\n----------SAVING-----------\n');
                // 删除缓存文件
                fs.unlink(req.files[0].path, function(err){
                    if (err){
                        return console.error(err);
                    }
                })
                // 将文件信息写入数据库
                var time = new Date().toJSON();

                var addSQL = 'INSERT INTO uploadfiles(fieldname, originalName, tmpName, encoding, mimetype, size, path, tmpPath, addTime) VALUES(?,?,?,?,?,?,?,?,?)';
                var addSqlParams = [req.files[0].fieldname, req.files[0].originalname, req.files[0].filename,
                    req.files[0].encoding, req.files[0].mimetype, req.files[0].size, des_file, __dirname + '/' + req.files[0].path, time];
                
                // 插入数据
                db.connection.query(addSQL, addSqlParams, function (err, result) {
                    if (err) {
                        return console.error(err);
                    }else {
                        console.log(JSON.stringify(response));
                        console.log(result);
                        console.log('\n----------SUCCEED----------\n\n');
                        res.json( response );
                    }
                })
            }
        });
    });
});

// 删除所有文件
app.get('/delete_all_file', function (req, res) {
    if (clear.clearAllFile()) {
        console.log('Clear all files successfully!');
        res.send('<h1>Clear all files successfully!</h1>');
    } else {
        res.send('<h1>Error: When clear all files!</h1>');
    }
})

// 删除缓存
app.get('/delete_cache', function (req, res) {
    if (clear.clearCache()) {
        console.log('Clear cache successfully!');
        res.send('<h1>Clear cache successfully!</h1>');
    } else {
        res.send('<h1>Error: When clear cache!</h1>');
    }
})

// 获取本地IPv4
function getLocalIPv4 () {
    interfaces = require("os").networkInterfaces();

    for(var devName in interfaces){//遍历所有连接
        var iface = interfaces[devName];
        for(var i=0; i<iface.length; i++){//遍历每个连接中的不同地址(family为标识)
            var alias = iface[i];
            if(alias.family == 'IPv4' && alias.address != '127.0.0.1' && !alias.internal)//该判断保证为有效的IPv4地址(排除了内部地址和本地地址)
            {
                return alias.address;
            }
        }
    }
}

// 监听
var server = app.listen(3333, getLocalIPv4(), function () {
    var host = server.address().address;
    var port = server.address().port;

    console.log('访问地址为:http://%s:%s', host, port);
});

依赖项,package.json

{
    "name": "node-file",
    "version": "0.0.1",
    "dependencies": {
        "body-parser": "^1.18.3",
        "express": "^4.16.3",
        "fs": "0.0.1-security",
        "multer": "^1.3.1",
        "mysql": "^2.16.0"
    }
}

clear.js

/**
 * 文件清理 + 缓存清理
 */
// 依赖
var fs = require('fs');
var db = require('./db');

var clearAllFile = function () {
    fs.readdir('./uploadFiles/file', function (error, files) {
        if (error) {
            return console.error(error);
        }
        for (var i=0; i<files.length; i++) {
            fs.unlink(__dirname + "/uploadFiles/file/" + files[i].toString(), function (err) {
                if (err) {
                    return console.error(err);
                }
            })
            // 在数据库中删除文件信息
            var dropSql = 'delete from uploadfiles where originalName=\''+ files[i].toString()+'\'';
            db.connection.query(dropSql, function (err, result) {
                if (err) {
                    return console.error(err);
                }
            })
        }
    })
    return true;
}

var clearCache = function () {
    fs.readdir('./uploadFiles/tmp', function (error, files) {
        if (error) {
            return console.error(error);
        }
        for (var i=0; i<files.length; i++) {
            fs.unlink(__dirname + "/uploadFiles/tmp/" + files[i].toString(), function (err) {
                if (err) {
                    return console.error(err);
                }
            })
        }
    })
    return true;
}

exports.clearAllFile = clearAllFile;
exports.clearCache = clearCache;

db.js

/**
 * 连接数据库
 */
var mysql = require('mysql');

var connection = mysql.createConnection({
    user: 'mysqljs',
    password: 'mysqljs',
    database: 'uploadfiles'
});
connection.connect();

exports.connection = connection;

2. 思路

整体思路:在前端选择文件并上传至服务器,服务器接收文件同时删除缓存;接收文件成功后,将文件信息读取存入数据库中;删除文件时,同步删除数据库中的相关信息。

详细步骤:

 首先我们需要依赖以下几个npm模块:

  1. express - 搭建服务器;
  2. mysql - 连接数据库;
  3. fs - 文件操作;
  4. body-parser - 解析post请求体;
  5. multer - 对multipart/form-data编码文件解析;

 然后关联两个自定义模块:clear和db,clear中包含了清除所有文件和清除所有缓存的两个方法,db则返回一个数据库连接;

var clear = require('./clear');
var db = require('./db');

 在当前文件夹创建文件夹uploadFiles,文件下下创建两个子文件夹tmpfile,用来存放临时文件和接收文件。然后设置tmp为临时文件的目录:

// 设置文件缓存的目录
var upload = multer({ dest: './uploadFiles/tmp/'});

 接着设置应用的请求响应,需要响应四种请求:

  • app.get('/') - 首页请求,使用res.sendFile()把首页丢过去;
  • app.post('/upload_file') - 上传文件请求(见下方解释)
  • app.get('/delete_all_file') - 删除所有文件请求,调用自定义模块clear中的clearAllFile()方法,通过返回值来判断是否成功;
  • app.get('/delete_cache') - 清除缓存请求,调用自定义模块clear中的clearCache()方法,通过返回值来判断是否成功;

2.1 文件上传详解

 响应/upload_file请求,使用upload.array('file')为所有接受到的文件添加统一的字段file

app.post('/upload_file', upload.array('file'), function (req, res) {
    // ...
})

req.files[0]包含了文件的全部信息(由中间件upload.array('file')写入的),内容如下:

{ fieldname: 'file',
  originalname: '542F2294A1566BEA83D1BAE1A00D6C9A.jpg',
  encoding: '7bit',
  mimetype: 'image/jpeg',
  destination: './uploadFiles/tmp/',
  filename: 'b0dcfcd9d88451bab4ca427571c77cd5',
  path: 'uploadFiles\\tmp\\b0dcfcd9d88451bab4ca427571c77cd5',
  size: 217804 }

 设置文件存储位置,为当前目录uploadFiles文件夹中file字段对应的文件夹,通过设置不同字段对文件进行分类存储可以保证较好的逻辑清晰性:

var des_file = __dirname + "/uploadFiles/" + req.files[0].fieldname + "/" + req.files[0].originalname;

 接下来通过读取编码文件来还原上传的文件,用到的方法如下结构:

app.post('/upload_file', upload.array('file'), function (req, res) {
    
    // ....
    fs.readFile(req.files[0].path, function (error, data) {
        
        // ....
        fs.writeFile(des_file, data, function (err) {
            if (err) {
                // ....
            }else {
                // ....
                // 删除缓存文件
                fs.unlink(req.files[0].path, function(err){
                    // ....
                })

                var addSQL = '';
                var addSqlParams = [];
                
                // 插入数据
                db.connection.query(addSQL, addSqlParams, function (err, result) {
                    // ....
                });
            }
        });
    });
});

2.2 自建模块clear和db

Node.js 连接 MySQL | 菜鸟教程

db.js文件结构简单,返回一个数据库连接,使用方法如下:

var db = require('./db');

db.connection.query(query1[, query2], callback(err, result){
    // ....
})

clear.js文件定义了两个方法:清除缓存和清除所有文件,清除所有文件除了清除磁盘文件外,还清空了数据库中的相关信息:

var clearAllFile = function () {
    // 获取目录信息
    fs.readdir('./uploadFiles/file', function (error, files) {
        // ....
        // 循环删除文件
        for (var i=0; i<files.length; i++) {
            fs.unlink(__dirname + "/uploadFiles/file/" + files[i].toString(), function (err) {
                // ....
            })
            // 在数据库中删除文件信息
            var dropSql = '';
            db.connection.query(dropSql, function (err, result) {
                // ....
            })
        }
    })
    return true;
}

// 接口
exports.clearAllFile = clearAllFile;
posted @ 2018-07-27 13:48  whuls  阅读(1019)  评论(0编辑  收藏  举报