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;

Nodejs学习笔记(5) 文件上传系统实例的更多相关文章

  1. SpringMVC:学习笔记(8)——文件上传

    SpringMVC--文件上传 说明: 文件上传的途径 文件上传主要有两种方式: 1.使用Apache Commons FileUpload元件. 2.利用Servlet3.0及其更高版本的内置支持. ...

  2. Django:学习笔记(8)——文件上传

    Django:学习笔记(8)——文件上传 文件上传前端处理 本模块使用到的前端Ajax库为Axio,其地址为GitHub官网. 关于文件上传 上传文件就是把客户端的文件发送给服务器端. 在常见情况(不 ...

  3. springmvc学习笔记--支持文件上传和阿里云OSS API简介

    前言: Web开发中图片上传的功能很常见, 本篇博客来讲述下springmvc如何实现图片上传的功能. 主要讲述依赖包引入, 配置项, 本地存储和云存储方案(阿里云的OSS服务). 铺垫: 文件上传是 ...

  4. Javaweb学习笔记10—文件上传与下载

    今天来讲javaweb的第10阶段学习.文件的上传与下载,今天主要说的是这个功能的实现,不用说了,听名字就是外行人也知道肯定很重要啦. 老规矩,首先先用一张思维导图来展现今天的博客内容.       ...

  5. Struts2学习笔记(十一)——文件上传

    1.单文件上传 单文件上传步骤: 1)创建上传jsp页面 文件上传的表单提交方式必须是POST方式,编码类型:enctype="multipart/form-data",默认是 a ...

  6. go web 第二天 学习笔记之文件上传

    package main import ( "crypto/md5" "fmt" "html/template" "io" ...

  7. SpringMVC学习笔记八:文件上传及多个文件上传

    SpringMVC实现文件上传需要加入jar包,commons-fileupload-1.3.1.jar,commons-io-2.2.jar 项目目录树: pom.xml加入需要的包 <pro ...

  8. Laravel 学习笔记之文件上传

    自定义添加磁盘——upload 位置:config/filesystems.php 'disks' => [ 'local' => [ 'driver' => 'local', 'r ...

  9. java学习笔记 (6) —— 文件上传

    1.新建upload.jsp <%@ page language="java" import="java.util.*" pageEncoding=&qu ...

随机推荐

  1. IFIX 5.9 历史数据 曲线 (非SQL模式)

    装完 ifix 5.9 默认是没有Hist 开头的 历史数据源的,没存,至少我装的版本是这样. 那个Historian 也没有安装包,好像还要授权,自己研究不了. 1 先把数据存本地 在你的安装包里 ...

  2. codeforces 876B

    B. Divisiblity of Differences time limit per test 1 second memory limit per test 512 megabytes input ...

  3. Leetcode(10)-正则表达式匹配

    给定一个字符串 (s) 和一个字符模式 (p).实现支持 '.' 和 '*' 的正则表达式匹配. '.' 匹配任意单个字符. '*' 匹配零个或多个前面的元素. 匹配应该覆盖整个字符串 (s) ,而不 ...

  4. Netty(二)Netty 与 NIO 之前世今生

    2.1 Java NIO 三件套 在 NIO 中有几个核心对象需要掌握:缓冲区(Buffer).选择器(Selector).通道(Channel). 2.1.1 缓冲区 Buffer 1.Buffer ...

  5. Gym 101128F Landscaping(网络流)题解

    题意:n*m的地,从有高地和低地,从高地走到低地或者从低地走到高地花费a,把高地和低地互相改造一次花费b.现在要走遍每一行每一列,问最小花费 思路:超级源点连接所有低地,容量b:所有地向四周建边,容量 ...

  6. How to enable HTTPS for local development in macOS using Chrome

    How to enable HTTPS for local development in macOS using Chrome HTTPS, macOS, Chrome local HTTPS htt ...

  7. 如何配置 webpack 支持 preload, prefetch, dns-prefetch

    如何配置 webpack 支持 preload, prefetch, dns-prefetch webpack , preload, prefetch https://webpack.js.org/p ...

  8. NGK团队是如何打造超高回报率的BGV项目的?

    NGK旨在激励网络的供给端引导去中心化的节点集群,用于促进网络使用和增加带宽流动. 具体来讲,在未来的24个月内,NGK会将部分生态参与者纳入白名单系统有兴趣的参与者可关注官方信息. 对内,NGK采用 ...

  9. 若依管理系统RuoYi-Vue(二):权限系统设计详解

    若依Vue系统中的权限管理部分的功能都集中在了系统管理菜单模块中,如下图所示.其中权限部分主要涉及到了用户管理.角色管理.菜单管理.部门管理这四个部分. 一.若依Vue系统中的权限分类 根据观察,若依 ...

  10. 使用 Castle 实现 AOP,以及 Autofac 集成 Castle

    Castle 是 2003 年诞生于 Apache Avalon 项目,目的是为了创建一个IOC 框架.发展到现在已经有四个组件: ORM组件:ActiveRecord IOC组件:Windsor 动 ...