目标:
在上一篇文章中,我们简单介绍了如何使用
nginx
对页面及文件夹进行代理,我们本篇来简单介绍一下,前端如何使用node定义一个上传接口,并上传和删除文件。
环境
我们本篇采取的是使用node
进行文件管理,所以,node
环境就是不可缺少的了,那我们安装完了node
,还需要准备什么呢?
node
作为一个V8引擎的JavaScript运行环境
,可以在服务器端运行JavaScript代码
,并且已经广泛应用于Web服务器、实时通信应用、微服务架构、大数据处理等领域,已经成为前端开发人员必备的技能之一。它采用事件驱动
、非阻塞I/O模型
,使得可以处理大量并发连接
而不会导致阻塞,适用于构建高性能、可扩展的网络应用,它提供了大量的模块,例如:fs
、http
、path
、events
、util
、os
、crypto
、stream
、child_process
、express
等等。这里我们只罗列了一些ndoe
常用的模块,但是绝对不仅仅是这些,可千万不要小瞧了它,今天我们就用node来实现一个文件的管理。
express
express
是一个流行的Node.js Web应用框架,它简化了构建Web应用程序的过程。它提供了一组简洁灵活的API,使开发者能够轻松地处理HTTP请求、路由、中间件等。以下是一些express
框架的主要特点和功能:
- 简洁而灵活 :
express
提供了一组简洁而灵活的API,使得开发者能够快速构建Web应用程序。它遵循"最小化即最佳化"的设计原则,只提供了核心的功能,而其他高级功能则可以通过中间件和插件进行扩展。 - 路由处理 :
express
提供了灵活的路由处理机制,可以根据不同的URL路径和HTTP方法来匹配和处理请求。通过定义不同的路由,可以将请求分发到相应的处理函数,并实现不同URL路径的路由映射。 - 中间件 :
express
支持使用中间件来处理请求和响应。中间件是一个函数,可以在请求到达路由处理函数之前或之后进行一些操作,如身份验证、日志记录、错误处理等。中间件可以串联使用,形成一个处理请求的管道。 - 模板引擎 :
express
支持使用各种模板引擎来生成动态的HTML页面。可以使用ejs
、pug
、handlebars
等模板引擎来渲染页面,并将动态数据插入到模板中。 - 静态文件服务 :
express
可以轻松地提供静态文件服务,如图片、CSS、JavaScript等。通过使用express.static
中间件,可以指定一个目录,将其中的静态文件直接提供给客户端。 - 错误处理 :
express
提供了一套错误处理机制,可以捕获和处理请求处理过程中的错误。可以通过定义错误处理中间件来统一处理错误,并返回适当的错误响应。 - 扩展性 :
express
是一个非常灵活的框架,可以通过使用第三方中间件和插件来扩展其功能。有许多第三方中间件可供选择,如body-parser
、cookie-parser
、passport
等,可以用于处理请求体解析、Cookie解析、用户认证等。
multer
multer
是一个Node.js的中间件模块,用于处理HTTP请求中的文件上传。它基于busboy
库实现,可以方便地处理表单中的文件上传操作。以下是multer
模块的主要特点和功能:
- 文件上传 :
multer
提供了方便的API来处理文件上传操作。它可以处理单个文件上传、多个文件上传,以及混合文件和其他数据的表单上传。 - 配置选项 :
multer
提供了一些配置选项,可以根据需求进行定制。可以设置文件上传的最大大小、文件类型限制、保存路径等。 - 中间件 :
multer
可以作为一个中间件函数在Express等Web框架中使用。通过将multer
中间件插入到路由处理函数之前,可以在请求到达路由处理函数之前对上传的文件进行处理。 - 文件信息 :
multer
通过req.file
属性提供了上传文件的信息。可以访问文件的原始名称、保存名称、大小、MIME类型等。 - 多存储引擎支持 :
multer
支持多种存储引擎,可以将上传的文件保存到不同的位置,如本地文件系统、内存、云存储等。可以根据需求选择适合的存储引擎。
path
path
是一个Node.js内置模块,用于处理文件路径和目录路径。它提供了一组方法,可以方便地处理和操作文件路径的字符串。以下是path
模块的主要方法和功能:
- 路径拼接 :
path.join()
方法可以将多个路径片段拼接成一个完整的路径。它会根据操作系统的规范自动处理斜杠和反斜杠的差异。 - 路径解析 :
path.parse()
方法可以解析一个路径字符串,并返回一个包含路径各个部分的对象。可以通过该对象访问路径的根目录、目录名、文件名、扩展名等信息。 - 路径规范化 :
path.normalize()
方法可以将一个路径字符串规范化,消除冗余的斜杠和点。它会处理路径中的相对路径和上级目录,返回一个规范化后的路径。 - 路径相对化 :
path.relative()
方法可以根据当前工作目录计算出两个路径之间的相对路径。它会返回一个相对于第一个路径的相对路径。 - 路径解析和拼接 :
path.resolve()
方法可以将多个路径解析为一个绝对路径。它会根据当前工作目录解析相对路径,并处理上级目录和根目录。 - 路径分隔符 :
path.sep
属性是当前操作系统的路径分隔符。在Windows上为反斜杠``,在POSIX系统上为斜杠/
。 - 文件扩展名 :
path.extname()
方法可以返回一个路径的文件扩展名。它会提取路径中最后一个点后面的部分作为扩展名。
单/多文件上传
其实我们在node连接数据库并进行查询,编辑(点击跳转)
这篇文章中已经提过荣誉和进行接口的定义,使用的同样也是express
,因此我们可以借鉴上一篇文章的借口定义,只不过,我们之前调取接口时候,采用的是query
拼接,或者是通过body
传参一个对象到服务端,而作为文件上传,我们同样采用的是post格式的body传参,只不过这里body里面的传参,并不是单纯的对象,而是字段的key
为file
,value
为formData
的格式的对象。
在上面,我们也介绍了这次用到的几个模块分别是做什么的,下面我们就直接上代码:
js
const express = require('express');
const multer = require('multer');
const path = require('path');
const app = express();
const storage = multer.diskStorage({
// 因为我需要上传到上一篇建立的nginx的代理文件夹下,所以不能使用相对路径
// 使用path.join()来把路径直接拼接起来
destination: path.join('/Users/wangtianxiang/Desktop/fileServe/', 'upload/'),
filename: (req, file, cb) => {
const ext = path.extname(file.originalname);
const fileName = file.originalname.replace(ext, '');
cb(null, fileName + ext); // 拼接文件名+后缀
}
});
// multer在保存上传的文件时,默认情况下不会保留文件的后缀名,14行拼接的原因
const upload = multer({ storage });
app.post('/file/upload', upload.single('file'), (req, res) => {
// req.file 是上传的文件信息
res.send('文件上传成功');
});
app.listen(3000, () => {
console.log('服务器已启动');
});
到这里,一个简单的上传的服务就写好了,我们只需要启动这个服务就可以了,接下来我们来试试能不能达到我们预期的效果:
可以看到,我们现在单个文件上传已经可以实现了,并能够在指定文件夹中访问到,但是我们多文件上传的时候,会报错如下,因为目前还没有进行多文件上传的配置
这时候,我们需要将upload.single
替换成upload.array
,第一个参数依旧是file
,第二个参数为多文件上传数量限制,完整代码如下:
js
const express = require('express');
const multer = require('multer');
const path = require('path');
const app = express();
const storage = multer.diskStorage({
destination: path.join('/Users/wangtianxiang/Desktop/fileServe/', 'upload/'),
filename: (req, file, cb) => {
const ext = path.extname(file.originalname);
const fileName = file.originalname.replace(ext, '');
cb(null, fileName + ext); // 拼接文件名+后缀
}
});
const upload = multer({ storage });
app.post('/file/upload', upload.array('file', 5), (req, res) => {
// req.files 是上传的文件信息的数组
res.send('文件上传成功');
});
app.listen(3000, () => {
console.log('服务器已启动');
});
我们来看一下,是否达到了预期的效果:
可以看到,我们多选的额文件,都是成功了的,到这里,文件上传也就告一段落了。
文件删除
说到文件删除,我们就必须要用到fs
模块了,fs
模块是Node.js
的文件系统模块,用于对文件系统进行操作。它提供了一系列的方法,用于读取、写入、删除、重命名等文件系统操作。下面我们直接上代码:
js
const fs = require('fs');
const path = require('path');
const express = require('express');
const app = express();
const folderPath = path.join('/Users/wangtianxiang/Desktop/fileServe/', 'upload/');
app.delete('/file/delete', (req, res) => {
console.log(req)
fs.readdir(folderPath, (err, files) => {
if (err) return res.status(500).send('读取文件夹失败');
files.forEach((file) => {
if (file === req.query.filename) {
const filePath = path.join(folderPath, file);
fs.unlink(filePath, (err) => {
if (err) return (res.send(`删除文件失败:${err}`))
res.send(`文件 ${file} 删除成功`);
});
}
});
});
});
app.listen(3000, () => {
console.log('服务器已启动');
});
在这和段代码中,我们从指定文件夹中,删除了指定文件名的文件,下面我们通过apiFox
进行接口调用:
在这个文件夹下,我们有三个文件,我们删除snake.html
如下图:
可以看到,我们已经从文件夹中,将snake.html
文件删除成功。至于对文件删除,经过最近三篇文章,大家应该也知道怎么删除了,同样的逻辑,只不过我们可以换成post
请求方式,传入一个数组,从req
中获取,循环传入需要删除的数组,调用fs.unlink
进行删除即可。
至此,文件的上传,查看,删除就都完成了,如果需要将文件管理做成一个界面版的,需要结合最近三篇文章,这样我们就可以在浏览器中,对文件夹文件进行管理了。
完整代码
js
const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const app = express();
const folderPath = path.join('/Users/wangtianxiang/Desktop/fileServe/', 'upload/');
const storage = multer.diskStorage({
destination: folderPath,
filename: (req, file, cb) => {
const ext = path.extname(file.originalname);
const fileName = file.originalname.replace(ext, '');
cb(null, fileName + ext); // 拼接文件名+后缀
}
});
const upload = multer({ storage });
app.post('/file/upload', upload.array('file', 5), (req, res) => {
// req.files 是上传的文件信息的数组
res.send('文件上传成功');
});
app.delete('/file/delete', (req, res) => {
console.log(req)
fs.readdir(folderPath, (err, files) => {
if (err) return res.status(500).send('读取文件夹失败');
files.forEach((file) => {
if (file === req.query.filename) {
const filePath = path.join(folderPath, file);
fs.unlink(filePath, (err) => {
if (err) return (res.send(`删除文件失败:${err}`))
res.send(`文件 ${file} 删除成功`);
});
}
});
});
});
app.listen(3000, () => {
console.log('服务器已启动');
});