上一章讲到前端文件下载功能的实现,之前也讲过前端文件上传功能的实现,这一章就讲一下后端怎么接收前端上传的文件,以及怎么实现文件下载功能。
往期文章
这里使用的技术栈为:nodejs + express + multer,如果你不熟悉这些技术栈,可以先去了解一下。
multer 相关的还没写过,但是很简单,就是调用几个
api,这篇文章中也会介绍使用方式,具体可以看看官方文档,传送门
一、项目结构
什么也没有,就一个app.js
就够了,项目依赖的包也很少,就是express
和multer
。
app.js
文件内容如下:
const express = require('express');
const app = express();
app.use(express.static(__dirname + '/public'));
app.listen(3000, () => {
});
然后在public
目录下新建一个index.html
文件,这个就是前端相关的内容了,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传和文件下载</title>
</head>
<body>
<div>
<h1>文件上传</h1>
<input type="file" name="file">
<input type="submit" value="上传" id="upload">
</div>
<hr/>
<div>
<h1>文件下载</h1>
<input type="button" value="下载" id="download">
</div>
<script>
// 上传
document.getElementById('upload').onclick = function () {
var file = document.querySelector('input[type=file]').files[0];
var formData = new FormData();
formData.append('file', file);
var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.send(formData);
};
// 下载
document.getElementById('download').onclick = function () {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/download');
xhr.send();
xhr.responseType = 'blob';
xhr.onload = function () {
var blob = xhr.response;
var a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'download.jpg';
a.click();
}
}
</script>
</body>
</html>
这里没有使用
file-type
来判断文件类型,这里就是一个简单的文件上传和下载功能,如果你想要判断文件类型,可以自己去实现一下。
上的环境都准备好了,控制台输入node app.js
,然后访问http://localhost:3000/index.html
,如果可以看到你写的页面,那么就说明环境搭建成功了。
二、文件上传
前端就不讲了,可以参考之前的文章,上面也列出了代码。
使用nodejs
来接收前端上传的文件,首先肯定是需要有一个接口来接收的,这里就是/upload
接口,接口的代码如下:
// 引用multer
const multer = require('multer');
// 创建上传文件的中间件
const upload = multer().single('file');
// 上传文件的接口
app.post('/upload', upload, (req, res) => {
res.send(req.file);
});
这里使用的是multer
来接收前端上传的文件,multer
是一个nodejs
的中间件,用来处理multipart/form-data
类型的表单数据,它主要用于上传文件,但是也可以用于其他用途。
multer 的使用
这里是multer
的最简单的使用方式,上面multer().single('file')
这个就是创建一个上传文件的中间件,single
表示只接收一个文件,file
表示前端上传文件的name
属性值。
它还可以有其他的使用方式,比如:
// 接收多个文件
multer().array('file', 5);
// 接收任意数量的文件
multer().any();
// 接收任意数量的文件,但是限制文件的大小
multer({
limits: {
fileSize: 1024 * 1024}}).any();
上面的示例中看到了multer
还可以限制文件的大小,这里的limits
就是限制的配置,那就肯定还有其他的配置,比如:
dest 或者 storage
:用来配置上传文件的存储路径,这个属性是一个函数,函数的参数有三个:req
:请求对象file
:上传的文件对象cb
:回调函数,用来配置上传文件的存储路径
fileFilter
:用来配置上传文件的过滤器,这个属性是一个函数,函数的参数有三个:req
:请求对象file
:上传的文件对象cb
:回调函数,用来配置上传文件的过滤器
limits
:用来配置上传文件的大小限制,这个属性是一个对象,里面有两个属性:fieldNameSize
:用来配置上传字段名称的大小限制,默认为100
个字节fieldSize
:用来配置上传字段值的大小限制,默认为1MB
fields
:用来配置上传字段非文件的数量限制,默认为Infinity
fileSize
:用来配置上传文件的大小限制,默认为Infinity
files
:用来配置上传文件的数量限制,默认为Infinity
parts
:用来配置上传字段和文件的数量限制,默认为Infinity
headerPairs
:用来配置上传字段和文件的数量限制,默认为2000
const options = {
dest: (req, file, cb) => {
// 配置上传文件的存储路径,这里需要自己创建一个文件夹
cb(null, __dirname + 'public/uploads');
},
fileFilter: (req, file, cb) => {
// 配置上传文件的过滤器
cb(null, true);
},
limits: {
fieldNameSize: 100,
fieldSize: 1024 * 1024,
fields: Infinity,
fileSize: 1024 * 1024,
files: Infinity,
parts: Infinity,
headerPairs: 2000
}
};
const upload = multer(options).single('file');
multer 的进阶使用
你以为上面讲完配置,multer
就这些功能?不,它还有更多的功能,比如:
1. 文件的存储方式
上面虽然讲到了dest 或者 storage
可以配置上传文件的存储路径,但是它只能配置上传文件的存储路径,不能配置上传文件的存储方式,比如:
const options = {
dest: (req, file, cb) => {
// 配置上传文件的存储路径,这里需要自己创建一个文件夹
cb(null, __dirname + 'public/uploads');
}
};
const upload = multer(options).single('file');
这种方式配置出来的上传文件的存储方式是DiskStorage
,也就是存储在硬盘上,如果你只是想接收上传的文件进行一些操作,或者不想存在硬盘上,那么这种方式就不合适。
那么怎么配置上传文件的存储方式呢?multer
提供了两种存储方式:
DiskStorage
:存储在硬盘上MemoryStorage
:存储在内存中
const options = {
storage: multer.diskStorage({
destination: (req, file, cb) => {
// 配置上传文件的存储路径,这里需要自己创建一个文件夹
cb(null, __dirname + '/public/uploads');
},
filename: (req, file, cb) => {
// 配置上传文件的文件名
cb(null, file.originalname);
}
})
};
const upload = multer(options).single('file');
这里也就补充了storage
配置的使用方式,它是一个对象,需要使用multer.diskStorage
或者multer.memoryStorage
来配置上传文件的存储方式,然后再配置destination
和filename
来配置上传文件的存储路径和文件名。
multer.diskStorage
和multer.memoryStorage
都是函数,它们都接收一个对象作为参数,这个对象有两个属性:
destination
:用来配置上传文件的存储路径req
:请求对象file
:上传的文件对象cb
:回调函数,用来配置上传文件的存储路径
filename
:用来配置上传文件的文件名req
:请求对象file
:上传的文件对象cb
:回调函数,用来配置上传文件的文件名
这里的
cb
回调函数的第一个参数是error
,如果配置出错,就需要传入error
,否则传入null
,第二个参数是配置的结果。
2. 文件的过滤器
上面讲到了fileFilter
可以配置上传文件的过滤器,比如:
const options = {
fileFilter: (req, file, cb) => {
// 配置上传文件的过滤器
cb(null, true);
}
};
const upload = multer(options).single('file');
这个过滤器是用来过滤上传的文件的,比如只允许上传jpg
和png
格式的图片,那么就需要配置这个过滤器,它接收三个参数:
req
:请求对象file
:上传的文件对象cb
:回调函数,用来配置上传文件的过滤器
获取上传的文件
上面讲到了如何配置上传文件,那么如何获取上传的文件呢?在multer
中,它提供了一个single
方法,用来获取上传的文件,它接收一个参数,就是上传文件的name
属性,比如:
const upload = multer().single('file');
这里的file
就是上传文件的name
属性,配置好了之后,他会将上传的文件保存到req.file
中,比如:
const upload = multer().single('file');
app.post('/upload', upload, (req, res) => {
console.log(req.file);
res.send('上传成功');
});
这里的req.file
就是上传的文件对象,它有以下属性:
fieldname
:上传文件的name
属性originalname
:上传文件的原始文件名encoding
:上传文件的编码mimetype
:上传文件的MIME
类型destination
:上传文件的存储路径filename
:上传文件的文件名path
:上传文件的完整路径size
:上传文件的大小buffer
:上传文件的二进制数据stream
:上传文件的stream
对象field
:上传文件的name
属性
上传多个文件
上面讲到了如何上传单个文件,那么如何上传多个文件呢?在multer
中,它提供了一个array
方法,用来获取上传的多个文件,它接收两个参数,第一个参数是上传文件的name
属性,第二个参数是上传文件的最大数量,比如:
const upload = multer().array('file', 5);
上面文件上传已经讲够了,更多的使用方式可以自行尝试,上传的文件multer
保存都给你做好了,所以你只需要配置好上传的文件就可以了,接下来讲下载文件。
文件下载
文件下载很简单,相比于文件上传更简单,如果服务器存在这个文件可以直接下载,这个在我上面的配置中已经配置好了:
app.use(express.static(__dirname + '/public'));
就是这一句,它会将public
目录下的文件作为静态资源,所以你可以直接访问public
目录下的文件,然后前端就可以直接使用a
标签下载了,比如:
<a href="/download/1.jpg" download="1.jpg">下载</a>
这种方式就不过多的讲了,接下来讲一下如何使用Node.js
下载文件。
使用Node.js
下载文件
直接使用express
下载文件,需要使用res.download
方法,比如:
app.get('/download', (req, res) => {
const file = __dirname + '/public/1.jpg';
res.download(file);
});
上面的代码就是使用Node.js
下载文件的代码,它会将public
目录下的1.jpg
文件下载下来,这里的res.download
方法是express
提供的,它接收一个参数,就是文件的路径,它会自动的将文件下载下来,如果你想要自定义文件名,可以这样:
app.get('/download', (req, res) => {
const file = __dirname + '/public/1.jpg';
res.download(file, '2.jpg');
});
上面的代码就是将1.jpg
文件下载下来,并且重命名为2.jpg
,这里的重名并不是真正的重命名,它只是将文件的Content-Disposition
的filename
属性设置为2.jpg
,这个如果在a
标签中设置download
属性就失效了。
其他的下载方式
上面是使用了Express
提供的res.download
方法来下载文件,当然还有其他的方式可以下载文件,比如:
- 使用
fs
模块的createReadStream
方法来读取文件,然后使用res.write
方法来写入文件,最后使用res.end
方法来结束响应,这种方式比较麻烦,但是可以自定义响应头,比如:
app.get('/download', (req, res) => {
const file = __dirname + '/public/1.jpg';
const stream = fs.createReadStream(file);
res.writeHead(200, {
'Content-Type': 'application/octet-stream',
'Content-Disposition': 'attachment; filename=1.jpg'
});
stream.pipe(res);
});
下载网络文件
上面的代码都是下载本地文件,如果你想要下载网络文件,可以使用http
模块的get
方法来获取文件,然后使用res.write
方法来写入文件,最后使用res.end
方法来结束响应,比如:
app.get('/download', (req, res) => {
const http = require('http');
const url = 'http://www.baidu.com/img/bd_logo1.png';
http.get(url, (response: any) => {
res.writeHead(200, {
'Content-Type': 'application/octet-stream',
'Content-Disposition': 'attachment; filename=1.jpg'
});
response.pipe(res);
});
});
上面的代码就是下载百度的logo
图片,这里的http.get
方法接收两个参数,第一个参数是文件的地址,第二个参数是回调函数,回调函数接收一个参数,就是http
模块的response
对象,它是一个Readable
流,所以可以使用pipe
方法来将文件写入响应中。
总结
本文主要讲了如何使用Node.js
下载文件,包括本地文件和网络文件,可以说覆盖面还是比较广的,总体来说还是比较简单的,哈哈哈。