如何将大块传入二进制文件合并到视频(WebM)文件节点JS中?

时间:2019-06-30 15:34:10

标签: node.js buffer fs getusermedia arraybuffer

我正在尝试将base64的块上载到Node js服务器,并将这些块保存到一个文件中

let chunks = [];

app.post('/api', (req, res) => {
    let {blob} = req.body;
    //converting chunks of base64 to buffer
    chunks.push(Buffer.from(blob, 'base64'));
    res.json({gotit:true})

});

app.post('/finish', (req, res) => {
    let buf = Buffer.concat(chunks);
    fs.writeFile('finalvideo.webm', buf, (err) => {
        console.log('Ahh....', err)
    });
    console.log('SAVED')
    res.json({save:true})
});

上面的代码存在问题,视频无法播放,我不解释为什么我真的做错了什么,我也尝试了可写流,但也无法正常工作

更新-我

我已经实现了发送二进制文件的目的,而不是发送blob,即使我遇到类似TypeError: First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.

的问题

client.js

 postBlob = async blob => {
       let arrayBuffer = await new Response(blob).arrayBuffer();
        let binary = new Uint8Array(arrayBuffer)
        console.log(binary) // logging typed Uint8Array
        axios.post('/api',{binary})
            .then(res => {
                console.log(res)
            })

    };

server.js

 let chunks = [];

    app.post('/api', (req, res) => {
        let {binary} = req.body;



        let chunkBuff = Buffer.from(binary) // This code throwing Error
        chunks.push(chunkBuff);

        console.log(chunkBuff)

         res.json({gotit:true})

    });

//Somehow combine those chunks into one file
app.post('/finish', (req, res) => {
    console.log('Combinig the files',chunks.length);

     let buf = Buffer.concat(chunks);

    console.log(buf) //empty buff
    fs.writeFile('save.webm', buf, (err) => {
        console.log('Ahh....', err)
    });

    res.json({save:true})
});

更新-II

我能够接收二进制块并将其附加到流中,但是在最后的视频中,只有第一个块正在播放,我不知道其他块发生了什么,视频结束了。

代码

const writeMyStream = fs.createWriteStream(__dirname+'/APPENDED.webm', {flags:'a', encoding:null});

app.post('/api', (req, res) => {
    let {binary} = req.body;
 let chunkBuff = Buffer.from(new Uint8Array(binary));
    writeMyStream.write(chunkBuff);
res.json({gotit:true})

});

更新-III

我的客户代码|注意:我尝试了其他方式上传已注释掉的blob

     customRecordStream = stream => {



            let recorder = new MediaStreamRecorder(stream);
            recorder.mimeType = 'video/webm;codecs=vp9';


            recorder.ondataavailable = this.postBlob 
            recorder.start(INT_REC)

        };

 postBlob = async blob => {


        let arrayBuffer = await new Response(blob).arrayBuffer();
        let binary = new Uint8Array(arrayBuffer)


            axios.post('/api',{binary})
                .then(res => {
                    console.log(res)
                })
        // let binaryUi8 = new Uint8Array(arrayBuffer);
        // let binArr = Array.from(binaryUi8);
        // // console.log(new Uint8Array(arrayBuffer))
        //
        // console.log(blob);


        // console.log(binArr)

        // let formData = new FormData();
        // formData.append('fname', 'test.webm')
        // formData.append("file", blob);
        //
        // console.log(formData,'Checjk Me',blob)
        // axios({
        //     method:'post',
        //     url:'/api',
        //     data:formData,
        //     config: { headers: {'Content-Type': 'multipart/form-data' }}
        // }).then(res => {
        //     console.log(res,'FROM SERBER')
        //
        // })
        //
        //
        //     .then(res => {
        //         console.log(res)
        //     })

        // this.blobToDataURL(blob, (blobURL) => {
        //
        //     axios.post('/api',{blob:blobURL})
        //         .then(res => {
        //             console.log(res)
        //         })
        // })


    };

3 个答案:

答案 0 :(得分:2)

我可以通过使用FileReader api在前端将其转换为base64编码来使其工作。在后端,从发送的数据块创建一个新的Buffer并将其写入文件流。我的代码示例中的一些关键事项:

  1. 我使用fetch是因为我不想插入axios
  2. 使用fetch时,必须确保在后端使用bodyParser
  3. 我不确定您要在块中收集多少数据(即,传递给start对象上的MediaRecorder方法的持续时间值),但是您需要确保您的后端可以处理传入的数据块的大小。我将我的数据确实设置为50MB很高,但这可能没有必要。
  4. 我从不明确地关闭写入流……您可以在/final路由中执行此操作。否则,createWriteStream默认为自动关闭,因此node进程将自动执行此操作。

下面的完整示例:

前端

const mediaSource = new MediaSource();
mediaSource.addEventListener('sourceopen', handleSourceOpen, false);
let mediaRecorder;
let sourceBuffer;

function customRecordStream(stream) {
  // should actually check to see if the given mimeType is supported on the browser here.
  let options = { mimeType: 'video/webm;codecs=vp9' };
  recorder = new MediaRecorder(window.stream, options);
  recorder.ondataavailable = postBlob 
  recorder.start(INT_REC)
};

function postBlob(event){
  if (event.data && event.data.size > 0) {
    sendBlobAsBase64(event.data);
  }
}

function handleSourceOpen(event) {
  sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp8"');
} 

function sendBlobAsBase64(blob) {
  const reader = new FileReader();

  reader.addEventListener('load', () => {
    const dataUrl = reader.result;
    const base64EncodedData = dataUrl.split(',')[1];
    console.log(base64EncodedData)
    sendDataToBackend(base64EncodedData);
  });

  reader.readAsDataURL(blob);
};

function sendDataToBackend(base64EncodedData) {
  const body = JSON.stringify({
    data: base64EncodedData
  });
  fetch('/api', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body
  }).then(res => {
    return res.json()
  }).then(json => console.log(json));
}; 

后端:

const fs = require('fs');
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const server = require('http').createServer(app);

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json({ limit: "50MB", type:'application/json'}));

app.post('/api', (req, res) => {
  try {
    const { data } = req.body;
    const dataBuffer = new Buffer(data, 'base64');
    const fileStream = fs.createWriteStream('finalvideo.webm', {flags: 'a'});
    fileStream.write(dataBuffer);
    console.log(dataBuffer);
    return res.json({gotit: true});
  } catch (error) {
    console.log(error);
    return res.json({gotit: false});
  }
});

答案 1 :(得分:1)

灵感来自@willascend答案:

后端:

app.use(express.raw());
app.post('/video-chunck', (req, res) => {
    fs.createWriteStream('myvideo.webm', { flags: 'a' }).write(req.body);
    res.sendStatus(200);
});

前端:

mediaRecorder.ondataavailable = event => {
    if (event.data && event.data.size > 0) {
        fetch(this.serverUrl + '/video-chunck', {
            method: 'POST',
            headers: {'Content-Type': 'application/octet-stream'},
            body: event.data
        });
    }
};

我的快递版本是4.17.1

答案 2 :(得分:0)

我今天遇到了同样的问题 作为后端的解决方案,我使用了 fs.appendfile

fs.appendFile(Path, rawData, function (err) {
        if (err) throw err;
        console.log('Chunck Saved!');
    })