前言
在现代 Web 应用当中,文件上传和下载是非常常见的需求。但是在实际开发当中,我们往往面临许多问题,例如:如何正确的接受上传的文件?如何验证上传的文件格式和大小?如何高效地为用户提供文件下载?本文将介绍使用 Fastify 框架实现文件上传和下载的方法,为开发者提供一个参考方案。
环境准备
在开始本文之前,我们需要安装 Node.js 和 Fastify。如果您已经具备了这些条件,那么可以跳过本章。
安装 Node.js
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。它能够让 JavaScript 在后端运行,能够方便地实现服务器端的开发。如果您还没有安装 Node.js,可以到官网下载安装:https://nodejs.org。
安装 Fastify
Fastify 是一个快速和低开销的 Web 框架,它提供了一种简单而灵活的方式来构建 Web 应用。使用 Fastify 可以轻松地实现服务器端的开发。安装 Fastify,可以直接使用 npm 命令进行安装:
npm install fastify --save
文件上传
准备工作
在服务器端接受上传文件之前,我们需要做一些准备工作。首先,我们需要安装 multer 中间件。Multer 是一个用于处理文件上传的中间件,可以将客户端上传的文件保存到指定的路径当中。
使用 npm 命令进行安装:
npm install multer --save
接下来,我们需要在 Fastify 应用程序中,注册 multer 中间件。在我们的例子中,我们假设客户端将文件上传到 /upload 目录下。
// javascriptcn.com 代码示例 const fastify = require('fastify')() const multer = require('multer') const upload = multer({ dest: './upload' }) fastify.post('/upload', { preHandler: upload.single('file') }, async (req, res) => { console.log(req.file) res.send('ok') }) fastify.listen(3000, () => console.log('Server listening at http://localhost:3000'))
在上述代码中,我们首先使用 require 导入 fastify 和 multer 模块。接着,我们新建了一个 multer 实例,并指定了上传的目录。然后,在 Fastify 应用程序中,我们注册了一个 POST 请求处理程序,它在处理请求之前,使用 preHandler 属性来指定 multer 处理上传的文件,并使用 upload.single('file') 方法规定只允许上传一个名为 file 的文件。
在上传文件之后,我们需要对上传的文件进行处理。req.file 对象包含了上传文件的详细信息,可以用它来进行文件处理。
文件验证
在实际开发当中,我们往往需要验证上传文件的大小和格式,以避免出现一些不必要的安全问题。在 Fastify 框架下,我们可以使用 joi 中间件来实现文件验证。使用 joi 需要先安装:
npm install joi --save
然后,在我们的代码当中进行如下配置:
// javascriptcn.com 代码示例 const fastify = require('fastify')() const multer = require('multer') const joi = require('joi') const upload = multer({ dest: './upload' }) fastify.post('/upload', { preHandler: upload.single('file') }, async (req, res) => { const schema = joi.object({ file: joi.string().required(), size: joi.number().max(1024 * 1024).required(), mimetype: ['image/png', 'image/jpeg', 'application/pdf'] }) const validation = schema.validate({ file: req.file.originalname, size: req.file.size, mimetype: req.file.mimetype }) if (validation.error) { throw validation.error } console.log(req.file) res.send('ok') }) fastify.listen(3000, () => console.log('Server listening at http://localhost:3000'))
在上述代码当中,我们定义了一个对象 schema 来验证上传的文件格式和大小。然后,我们使用 validate 方法验证文件格式和大小是否符合 schema 规定。如果通过验证,那么我们就可以在之后的代码当中,使用 req.file 对象进行文件处理。
文件处理
在处理上传的文件的时候,我们往往需要把文件保存下来。在 Fastify 框架下,可以使用 fs 模块来实现文件的保存。我们可以在 POST 请求处理程序中,使用如下代码进行保存:
// javascriptcn.com 代码示例 const fastify = require('fastify')() const multer = require('multer') const joi = require('joi') const fs = require('fs') const upload = multer({ dest: './upload' }) fastify.post('/upload', { preHandler: upload.single('file') }, async (req, res) => { const schema = joi.object({ file: joi.string().required(), size: joi.number().max(1024 * 1024).required(), mimetype: ['image/png', 'image/jpeg', 'application/pdf'] }) const validation = schema.validate({ file: req.file.originalname, size: req.file.size, mimetype: req.file.mimetype }) if (validation.error) { throw validation.error } const { filename } = req.file const oldPath = `./upload/${filename}` const newPath = `./files/${filename}` fs.rename(oldPath, newPath, (error) => { if (error) { console.log(`rename error: ${error.message}`) throw new Error('文件保存失败') } else { console.log(`rename ${oldPath} -> ${newPath} succeed`) } }) res.send('ok') }) fastify.listen(3000, () => console.log('Server listening at http://localhost:3000'))
在上述代码当中,我们把上传的文件重命名,并把重命名后的文件保存到 ./files 目录下。使用 fs.rename 方法,可以实现文件的快速移动和重命名。
文件下载
在 Fastify 框架下,实现文件下载非常简单,我们只需要使用 fs 模块读取指定路径的文件,然后把文件内容作为响应返回给客户端即可。接下来,我们将实现文件下载的方法。
简单文件下载
首先,我们来实现一个简单的文件下载程序。在以下代码当中,我们发送了一个 GET 请求,请求的参数为文件的名称,从而返回指定文件内容。此处,我们默认文件保存在 ./files 目录下。
// javascriptcn.com 代码示例 const fastify = require('fastify')() const fs = require('fs') fastify.get('/download/:filename', async (req, res) => { const { filename } = req.params res.header('Content-Type', 'application/octet-stream') res.header('Content-Disposition', 'attachment;filename=' + filename) fs.createReadStream(`./files/${filename}`).pipe(res) }) fastify.listen(3000, () => console.log('Server listening at http://localhost:3000'))
在上述代码当中,我们首先在 GET 请求处理程序中,获取请求参数 filename。接着,我们设置了响应的 Content-Type 和 Content-Disposition 响应头,然后创建一个可读流对象,从指定文件中读取信息,并把读取到的信息通过管道传输到响应中。这就实现了一个简单的文件下载。
断点续传
上面实现的简单文件下载程序有一个问题,就是在下载大文件的时候,用户往往需要等待较长时间,而且当下载过程中网络连接发生中断时,需要重新开始下载。为了解决这个问题,我们可以使用 HTTP 协议的 Range 头来实现断点续传。
在 Fastify 框架下,我们可以使用 range-parser 中间件来实现 Range 头的解析:
npm install range-parser --save
然后,将以下代码添加到路由配置中:
// javascriptcn.com 代码示例 const range = require('range-parser') fastify.get('/download/:filename', async (req, res) => { const { filename } = req.params const filePath = `./files/${filename}` const fileInfo = fs.statSync(filePath) const { size } = fileInfo const rangeHeader = req.headers['range'] if (rangeHeader) { const ranges = range(size, rangeHeader) if (ranges === -1) { res.status(416).send('Range Not Satisfiable') } else { res.status(206).header('Accept-Ranges', 'bytes').header('Content-Range', `bytes ${ranges[0].start}-${ranges[0].end}/${size}`).header('Content-Length', `${ranges[0].end-ranges[0].start+1}`) fs.createReadStream(filePath, { start: ranges[0].start, end: ranges[0].end }).pipe(res) } } else { res.header('Content-Type', 'application/octet-stream') res.header('Content-Disposition', 'attachment;filename=' + filename) res.header('Content-Length', size) fs.createReadStream(filePath).pipe(res) } })
在上述代码中,我们首先通过 fs.statSync 方法获取文件信息,然后获取请求头中的 Range 信息。如果 Range 不为空,那么我们可以进行断点续传;否则,我们就发送整个文件的内容。
当采用断点续传的时候,我们会返回 HTTP 206 Partial Content 状态码。通过设置 Accept-Ranges 响应头,我们表示支持 Range 头。然后,我们根据 Range 头和文件大小,计算出 Content-Range 响应头和 Content-Length 响应头,然后发送指定范围内的数据。
总结
本文基于 Fastify 框架,介绍了如何实现文件上传和下载。在实现文件上传的过程中,我们使用了 multer 中间件来处理上传的文件,并使用 joi 中间件对上传的文件进行验证。在实现文件下载的过程中,我们使用了 fs 模块对指定文件进行读取,并使用 range-parser 中间件来实现 Range 头的解析,从而实现断点续传。这些内容可以帮助开发者更加轻松地在 Fastify 框架下实现文件上传和下载的功能。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/652d31917d4982a6ebe9c022