问题 使用node / express显示BASE64视频


所以,有点奇怪的问题。我有一堆媒体文件在mongo中保存为base64字符串,有些是图像,有些是视频。

我制作了一个用于获取媒体文件的API:

app.get('/api/media/:media_id', function (req, res) {
    media.findById(req.params.media_id)
        .exec(function (err, media) {
            if (err) {
                res.send(err);
            }

            var file = new Buffer(media.file, 'base64');
            res.writeHead(200, {'Content-Type': media.type, 'Content-Transfer-Encoding': 'BASE64', 'Content-Length': file.length});
            res.end(file);
        });
});

现在,图像没有问题。它们可以直接从API加载,也可以从前端调用API(例如 <img src="/api/media/23498423">

问题

如果我从前端获取视频,如图像 - 但使用视频或对象标记:

<video src="/api/media/3424525" controls></video>

没问题,但如果我直接从API加载视频:

http://localhost:8080/api/media/3424525

服务器进程崩溃,没有错误。它只是冻结了。我们不是在谈论巨大的视频文件 - 它是一个1.5MB的视频。

我正在测试的所有视频的标题中的媒体类型是 video/mp4。哦,只是要清楚:如果我对图像做同样的事情,一切都很完美。

编辑:

好的,正如@idbehold和@zeeshan建议的那样,我看了一下gridfs和gridfs-stream,为了我的应用程序的目的,这肯定是我应该首先使用的。但是,在我的应用程序中实现gridfs后,问题仍然存在。

app.get('/api/media/:media_id', function (req, res) {
    gfs.findOne({ _id: req.params.media_id }, function (err, file) {
        if (err) {
            return res.status(400).send(err);
        }
        if (!file) {
            return res.status(404).send('');
        }

        res.set('Content-Type', file.contentType);
        res.set('Content-Disposition', 'inline; filename="' + file.filename + '"');

        var readstream = gfs.createReadStream({
            _id: file._id
        });

        readstream.on("error", function (err) {
            console.log("Got an error while processing stream: ", err.message);
            res.end();
        });

        readstream.pipe(res);
    });
});

当我从前端,HTML标签中调用媒体文件(无论是图像还是视频)时,一切正常。但是,如果我直接在浏览器中加载视频(再次,从1.5mb到最大6mb的小视频),服务器进程会冻结。更清楚一点:我在Windows上测试,服务器应用程序(server.js)在控制台中运行。控制台和正在运行的进程是冻结的。我无法在节点应用程序中加载任何更多的页面/视图,我甚至无法停止/终止/关闭节点应用程序或控制台。


7208
2018-03-22 15:10


起源

对于一个1.5MB的视频文件,您现在打电话时的内存将达到3MB res.end(file)。您应该直接将视频直播 res 没有缓冲。你应该使用像Mongo的GridFS及其流API这样的东西。您可能还必须添加对字节范围请求的支持。 - idbehold
我不知道gridfs,我看看,谢谢。 - Brian Emilius
是的, GridStore API是你想要使用的。 - idbehold
如果文件确实很小,我不同意你想要GridFS。但真正的问题是为什么要使用base64编码? GridFS驱动程序的功能只是以块的形式分解并存储为 BinData 无论如何。当然,您可以直接在任何集合属性中存储和返回二进制数据(所有GridFS都在做)。所以我个人只是存储为二进制,然后创建一个句柄和 .pipe() 至 res。 - Blakes Seven
我看到了你的观点。答案有很多原因。我不知道你可以在mongo中存储二进制数据。 2.我可能需要在将来存储更大的文件,但我不知道16mb文件限制。 - Brian Emilius


答案:


使用直接向/从GridFS流式传输视频 GridFS的流 要么用 mongodb-native数据库实例 要么 猫鼬

var mongo = require('mongodb'),
    Grid = require('gridfs-stream'),
    db = new mongo.Db('yourDatabaseName', new mongo.Server("127.0.0.1", 27017)),
    gfs = Grid(db, mongo);

//store
app.post('/video', function (req, res) {
    req.pipe(gfs.createWriteStream({
        filename: 'file_name_here'
    }));
    res.send("Success!");
});

//get
app.get('/video/:vid', function (req, res) {
    gfs.createReadStream({
        _id: req.params.vid // or provide filename: 'file_name_here'
    }).pipe(res);
});

完整文件和运行项目:

克隆节点作弊 direct_upload_gridfs, 跑 node app 其次是 npm install express mongodb gridfs-stream


7
2018-03-26 08:40





真的是一个奇怪的问题......
我可能会离开,但它值得一试:

直接从浏览器打开URL时的一个不同之处是浏览器也会尝试获取 http://localhost:8080/favicon.ico (同时试图找到标签图标)。也许问题与你的视频代码无关,而是与其他一些路线有关,试图处理 /favicon.ico 请求?

你尝试过使用过吗? wget 要么 curl


4
2018-03-30 21:42



这值得研究。好决定。 - Brian Emilius
我做了一些广泛的测试,在应用程序进程崩溃的任何情况都没有提取favicon.ico的任何问题 - 这是一个很好的建议,所以+1为那:) - Brian Emilius


我不知道答案,也许这是一个愚蠢的建议,但你使用的是什么浏览器?也许微软的东西会导致问题...


2
2018-03-31 18:46



我测试了chrome,firefox,firefox dev,opera,ie9 + 10,以及带有相同结果的edge,同样在android chrome上 - Brian Emilius