使用NodeJS + Express + MongoDB / GridFS上传/流音频

我写这篇文章是为了回应我在尝试开发我的最后一年计算机科学项目时发现的信息不足。 我的项目是类似于Spotify的音乐媒体Web应用程序。

这篇文章代码存储库:https://github.com/richard534/nodeMongoAudioUploadStreamTest/tree/master

有一些教程可以解决这个问题,但是,几乎所有教程都使用了更多的抽象概念,例如“ gridfs-stream” npm软件包。

在本教程中,我将仅使用本机MongoDB驱动程序作为节点来处理与GridFS的交互。 这为您提供了更大的灵活性,并将帮助您控制引擎盖下实际发生的事情。

假设条件

  • 对mongodb的基本了解(启动mongod,查看集合)
  • Express和Express Router的基础
  • JavaScript基础(回调函数)

Npm模块依赖性(NPM软件包)

  • Express@4.13.3(nodejs Web应用程序框架,简化了RESTful API的创建)
  • Multer@1.3.0(用于处理多部分/表单数据请求)
  • Mongodb@2.2.33(官方的mongodb nodejs驱动程序)

节点模块依赖性

  • 流(处理数据流的节点模块)

让代码开始💻

创建一个新目录并运行npm init来创建一个package.json文件

  mkdir streamMusicTest 
cd streamMusicTest
npm初始化
npm install --save express mongodb multer
触摸index.js

完成此操作后,在文本编辑器中打开index.js文件,然后继续。

模块依赖

首先,您需要声明模块依赖性。 为了便于阅读,我将它们分为npm模块和nodejs模块。

初始化Express,Express Router和MongoDB

接下来,我们将初始化快速应用程序。 然后,我们将调用app.use('/tracks', trackRoute)将trackRoute路由器绑定到/tracks URL。

是时候将我们的应用程序连接到mongoDB了。 我成功连接到数据库后,在文件的模块范围内声明了一个名为db的变量,用于存储数据库的实例。

之后,我们使用MongoClient API; 它是节点mongodb驱动程序的一部分,用于连接到我们的本地mongodb数据库。

声明GET轨道流路线

下一步是声明将处理从我们的Web服务获取轨道流的REST路由。

我们将在trackRoute变量引用的快速路由器对象上使用get方法, trackRoute /tracks/:trackID URL上的GET请求创建处理程序。 Express会自动将URL的:trackID部分中存在的值绑定到.get方法的回调函数中的变量req.params.trackID

我们需要采用此方法的第一步是尝试将req.params.trackID变量中存在的字符串值转换为mongoDB ObjectID 。 进行此openDownloadStream的原因是因为我们在此函数中进一步使用的openDownloadStream方法需要将类型为objectID的变量传递给它。

我们将转换内容封装在try catch中,因为如果MongoDB驱动程序的ObjectID API无法将字符串转换为objectID时,它将抛出错误。

成功投射之后,我们将响应标头设置为“内容类型:音频/ mp3”和“接受范围:字节”。 这将帮助浏览器知道如何处理响应。 *注意:正确处理部分内容(客户端跳过曲目)不在本教程的讨论范围内,这是一篇有关该主题的好文章。

然后,我们创建一个名为bucket的变量,并使用mongodb模块中的GridFSBucket实例对其进行初始化。 我们传递先前初始化的db变量和一个对象常量,该对象常量声明我们要读取的存储桶名称。 在这种情况下,我们将存储桶命名为“ tracks”。

GridFSBucket对象上的openDownloadStream方法返回可读流,用于从gridfs流式传输文件数据。 我们只需要向其传递我们要流式传输的文件的trackID。

当我们说“可读流”时,我们指的是NodeJS Streams API中定义的可读流。

openDownloadStream方法返回的可读流发出大量openDownloadStream事件。 从Readable Stream Docs中可以看到,可读流发出五种类型的事件。 关闭,数据,结尾,错误和可读性。

就我们的目的而言,我们仅对数据,错误和结束事件感兴趣。 我们为每个可能的事件编写一个侦听器函数。

  downloadStream.on('data',(chunk)=> {res.write(chunk);}); 

我们必须添加一个数据事件监听器以启动流。 每次有块可用(块是文件的一部分)时,都会发出数据事件。 每次调用事件时,侦听器回调将传递一个块。 然后,在侦听器回调中,我们可以使用res.write函数将块发送到客户端。 执行此操作,直到可读流的数据用完为止。 此时将调用end事件。 res.write函数实际上不是express的一部分,我们直接调用Node HTTP API。

  downloadStream.on('end',()=> {res.end();}); 

一旦我们的可读流发出了结束事件,我们只需使用express res.end方法来结束响应过程。

  downloadStream.on('error',()=> {res.sendStatus(404);}); 

为了完整起见,我还为error事件编写了一个侦听器函数。 为了简单起见,如果发生错误,我只向客户端发送404。 在现实世界中,您将需要解析错误并返回更准确的错误消息。

宣告POST追踪路线

最后,是时候声明REST路由了,该路由将处理将曲目发布到我们的Web服务。

与上次使用分配给trackRoute变量的快速路由器实例trackRoute 。 使用路由器对象上的get方法,我们为/tracks URL上的GET请求创建一个处理程序。

而不是在上一个示例中使用诸如:trackID类的URL参数将数据传递到后端,我们将发送在标头中使用'multipart/formdata'内容类型的POST请求。 这将使我们能够向请求主体添加轨道名称和二进制轨道。

邮寄请求正文的另一种类型称为'application/x-www-form-urlencoded' 。 通常在我们只想将键值对发送到服务器时使用。 但是,它不允许发送二进制文件,因此我们必须使用'multipart/formdata'请求类型。

Express默认情况下不支持'multipart/formdata'请求。 为了处理我们的请求,我们必须使用一个名为Multer的npm软件包。

我们在后处理程序中要做的第一件事是使用多个选项初始化multer实例。 我将multer.memoryStorage的实例multer.memoryStorage给multer对象的storage选项。 这将告诉multer在处理上传的文件时将其存储在缓冲区中,从而防止文件被写入文件系统。 我还传递了一个称为“ limits”的对象文字。

限制对象说明我希望我的请求最多包含1个非文件字段,最大文件大小为6mb(以字节表示的文件大小),请求中最多1个文件,最多2个部分(files +字段)。 Multer将针对这些条件执行验证,如果失败则传递错误。

我们使用multers .single(fieldname)方法接受名称为track的单个文件。 我们将其余函数代码放在此方法的回调函数中。

然后,我们检查.single()方法的回调函数是否传递了err值,该值表示在验证器验证期间发生了错误。 为了简单起见,如果multer传递错误,我只向客户端返回400个“错误请求”响应。 然后,我检查请求正文是否包含名称键,因为multer验证规则不会对此进行检查。

由于我们在内存存储模式下使用Multer,因此文件作为Buffer类型的对象存储在内存中。 Multer将文件绑定到快速请求对象中的字段。 我们可以从req.file.buffer下的请求对象中检索它。

我们必须将包含文件的缓冲区转换为可读流,以便将其流式传输到gridFS。 首先使用从Node stream API导入的可读类来实现。 我们使用new Readable()创建一个新的可读对象。 Readables .push方法使我们可以将缓冲区推入可读文件。 最后,我们将null值推送到可读数据中,以表示数据结束。

与上一个示例相同,我们初始化GridFSBucket的实例并将其配置为使用本地mongodb数据库上的“ tracks”存储桶。

GridFSBuckets .openUploadStream()方法返回可写流,用于将缓冲区写入GridFS。 我们从请求主体传递轨道的名称(这将是gridFS中的文件名),并将其分配给一个名为uploadStream的变量。

我们在由可读.pipe()变量引用的可读对象上使用.pipe()方法,将其所有数据推送到可写的uploadStream 。 最终,这将开始将文件流传输到gridFS的过程。

最后,我们只为GridFSBucketWriteStream发出的“错误”和“完成”事件定义了两个侦听器函数(在GridFSBucket API上调用.openUploadStream返回GridFSBucketWriteStream的实例)。 如果出错,我只返回500,最后返回201“创建”消息。

让我们看看它的作品🕹

为了测试Web服务,我使用Postman。 通过在命令行中输入node index.js来启动节点应用程序。

开机自检路线

我们可以使用以下URL localhost:3005/tracks测试POST跟踪端点。 将请求类型设置为post,并将请求主体类型设置为form-data 。 最后,将名称和跟踪键值对添加到请求中,确保将跟踪值设置为键入“ file”,然后从文件系统中选择跟踪。