提问者:小点点

如何将传入的二进制文件块转换为视频(webm)文件节点js?


我正在尝试将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这样的问题:第一个参数必须是字符串、Buffer、ArrayBuffer、Array或类似数组的对象。

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})
});

更新-二

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

代码

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})

});

更新-三

我的客户端代码|注意:我尝试过其他方法来上传我已经注释掉的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个答案

匿名用户

我能够通过在前端使用FileReaderapi转换为base64编码来实现此工作。在后端,从发送的数据块创建一个新的Buffer并将其写入文件流。我的代码示例中的一些关键内容:

  1. 我正在使用fetch,因为我不想拉入axios
  2. 使用fetch时,您必须确保在后端使用body Parser
  3. 我不确定您在块中收集了多少数据(即传递给MediaRecorder对象上的start方法的持续时间值),但您需要确保您的后端可以处理传入的数据块的大小。我将我的设置为非常高的50MB,但这可能不是必需的。
  4. 我从不显式关闭写入流…您可能会在/最终路由中执行此操作。否则,createWriteStream默认为自动关闭,因此节点进程将自动执行此操作。

完整的工作示例如下:

前端:

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});
  }
});

匿名用户

灵感来自@wilascend答案:

后端侧:

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

匿名用户

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

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