Javascript 如何使用流将MediaRecorder Web API输出保存到磁盘

Javascript 如何使用流将MediaRecorder Web API输出保存到磁盘,javascript,node.js,stream,electron,mediarecorder-api,Javascript,Node.js,Stream,Electron,Mediarecorder Api,我正在尝试内部(因此)并希望将输出作为流处理。作为流处理将允许我在保存到磁盘之前处理MediaRecorder输出,例如,我可以对其进行加密。对于我的特定用例,我只关心音频,所以我没有任何视频元素记录 我最基本的用例是使用流将输出保存到磁盘,但我似乎无法完成这项基本任务,因此我将重点讨论如何实现这一点 问题:如何使用流将MediaRecorder Web API输出保存到磁盘。 我可以使用下载“hack”将文件保存到磁盘,并成功地使用打开、转换(加密)、保存新的加密文件和删除未加密的文件。这意味

我正在尝试内部(因此)并希望将输出作为流处理。作为流处理将允许我在保存到磁盘之前处理MediaRecorder输出,例如,我可以对其进行加密。对于我的特定用例,我只关心音频,所以我没有任何视频元素记录

我最基本的用例是使用流将输出保存到磁盘,但我似乎无法完成这项基本任务,因此我将重点讨论如何实现这一点

问题:如何使用流将MediaRecorder Web API输出保存到磁盘。

我可以使用下载“hack”将文件保存到磁盘,并成功地使用打开、转换(加密)、保存新的加密文件和删除未加密的文件。这意味着我最终必须将未加密的数据保存到磁盘。即使在很短的时间内,这感觉像是一种安全上的妥协,我认为在保存之前加密是很容易避免的

在不同的流对象之间有相当多的交叉线,这是一个风险,但我很惊讶我还没有在线找到解决方案-因此我突然提出了堆栈溢出问题

下面是一个突出显示我所有尝试的项目。中的关键代码是record.js,位于save()函数中

最后,我尝试创建一个合适的
readStream
来插入使用
const writeStream=fs.createWriteStream(fPath)创建的
writeStream
使用
readStream.pipe(writeStream)

总之,我尝试了以下方法:

1<代码>Blob
readStream

我无法将
Blob
转换为
readStream
,只能将
ReadableStream
ReadableStreamDefaultReader
Uint8Array

2
Blob
文件(内存中),然后使用
fs.createReadStream()

我似乎无法在
fs.createReadStream(url)
中使用
ObjectURL
,它坚持附加本地路径。 对的回答表明,这是
fs.createReadStream()
的限制,使用
http.get()
request()
不适合我的情况,因为我没有尝试访问远程资源

3
Blob
buffer
,然后使用
fs.createReadStream()

我无法将
Blob
转换为可在
fs.createReadStream(buffer)
中使用的
buffer
,只能转换为
arrayBuffer
或具有
null
字节的缓冲区

非常感谢您的帮助


项目:

节点12.13.0、铬80.0.3987.158和电子8.2.0

设置:

  • 这四个文件:main.js、package.json、index.html、record.js都是项目文件夹中的单级文件
每个文件的内容:

package.json:

main.js:

index.html:


你好,世界!
你好,世界!
我们正在使用node document.write(process.versions.node),
Chrome文档.write(进程.版本.Chrome),
和Electron document.write(process.versions.Electron)。


记录 记录器状态:非活动

record.js:

console.log(“helloworld from record.js()”);
const remote=require('electron')。remote;
const path=require('path');
常数fs=要求('fs');
const appDir=remote.app.getPath('userData');
var recButton=document.getElementById(“button_rec”);
var recStatusSpan=document.getElementById(“rec_status”);
无功记录仪;
init=异步函数(){
//html页面事件处理程序:
addEventListener(“单击“,()=>{record()}”);
//设置媒体记录器:
var audioStream=await navigator.mediaDevices.getUserMedia({audio:true});
记录器=新的媒体记录器(audioStream,{mimeType:'audio/webm'});
块=[];
recorder.onstart=(事件)=>{
// ...
}
recorder.ondataavailable=(事件)=>{
推送(事件数据);
}
recorder.onstop=异步(事件)=>{
让fileName=`audiofile_${Date.now().toString()}.webm`;
//下载(块,文件名);//好的,我破解了它…
最终,挑战的关键在于:

如何将node.js中的
blob
转换为
readablestream

总之,我发现有效的步骤是:
blob
arrayBuffer
array
buffer
readStream

我需要以下函数将缓冲区转换为流。并且:

其余转换步骤为一行程序,完整保存功能如下:

save = async function (audioToSave, fPath) {
    console.log(`Trying to save to: ${fPath}`);

    // create the writeStream - this line creates the 0kb file, ready to be written to
    const writeStream = fs.createWriteStream(fPath);
    console.log(writeStream); // WriteStream {...}

    // The incoming data 'audioToSave' is an array containing a single blob of data.
    console.log(audioToSave); // [Blob]

    // Lets convert the data to a Blob
    var audioBlob = new Blob(audioToSave, {
        type: "audio/webm"
    });
    console.log(audioBlob); // Blob {size: 17955, type: "audio/webm"}
    // note: audioBlob = audio[0] has same effect

    // now we go through the following process: blob > arrayBuffer > array > buffer > readStream:
    const arrayBuffer = await audioBlob.arrayBuffer();
    console.log(arrayBuffer); // ArrayBuffer(17955) {}

    const array = new Uint8Array(arrayBuffer);
    console.log(array); // Uint8Array(17955) [26, 69, ... ]

    const buffer = Buffer.from(array);
    console.log(buffer); // Buffer(17955) [26, 69, ... ]

    let readStream = bufferToStream(buffer);
    console.log(readStream); // Readable {_readableState: ReadableState, readable: true, ... }

    // and now we can pipe:
    readStream.pipe(writeStream);

}
最后,我可以在数据和存储之间使用管道并继续使用其他流函数,例如加密。:)


希望这对其他人也有帮助。

最终,我不需要创建一个
ReadStream
,只需要这样一个
缓冲区:
让audio=audioToSave[0];
const audioArrayBuffer=wait audio.arrayBuffer();
const audioArray=new Uint8Array(audioArrayBuffer)
const audioBuffer=Buffer.from(audioArray);
点击:
const writeStream=fs.createWriteStream(fPath);
writeStream.write(audioBuffer);
现在我可以保存了,避免了“黑客”,在保存之前还没有通过加密。。。
const { app, BrowserWindow, ipcMain } = require('electron');

function createWindow () {
  // Create the browser window.
  let win = new BrowserWindow({
    width: 1000,
    height: 800,
    x:0,
    y:0,
    title: "Media Recorder Example",
    webPreferences: {
      nodeIntegration: true,
      devTools: true
    } 
  })
  win.openDevTools();
  win.loadFile('index.html')
}

app.whenReady().then(createWindow)
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
    <!-- https://electronjs.org/docs/tutorial/security#csp-meta-tag -->
    <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using node <script>document.write(process.versions.node)</script>,
    Chrome <script>document.write(process.versions.chrome)</script>,
    and Electron <script>document.write(process.versions.electron)</script>.
    <br/><br/>
    <div>
      <button id="button_rec">Record</button>
      <p>recorder state: <span id="rec_status">inactive</span></p>
    </div>
  </body>

  <script src="record.js"></script>

</html>
let { Readable } = require('stream') ;

function bufferToStream(buffer) {
    let stream = new Readable ();
    stream.push(buffer);
    stream.push(null);
    return stream;
}
save = async function (audioToSave, fPath) {
    console.log(`Trying to save to: ${fPath}`);

    // create the writeStream - this line creates the 0kb file, ready to be written to
    const writeStream = fs.createWriteStream(fPath);
    console.log(writeStream); // WriteStream {...}

    // The incoming data 'audioToSave' is an array containing a single blob of data.
    console.log(audioToSave); // [Blob]

    // Lets convert the data to a Blob
    var audioBlob = new Blob(audioToSave, {
        type: "audio/webm"
    });
    console.log(audioBlob); // Blob {size: 17955, type: "audio/webm"}
    // note: audioBlob = audio[0] has same effect

    // now we go through the following process: blob > arrayBuffer > array > buffer > readStream:
    const arrayBuffer = await audioBlob.arrayBuffer();
    console.log(arrayBuffer); // ArrayBuffer(17955) {}

    const array = new Uint8Array(arrayBuffer);
    console.log(array); // Uint8Array(17955) [26, 69, ... ]

    const buffer = Buffer.from(array);
    console.log(buffer); // Buffer(17955) [26, 69, ... ]

    let readStream = bufferToStream(buffer);
    console.log(readStream); // Readable {_readableState: ReadableState, readable: true, ... }

    // and now we can pipe:
    readStream.pipe(writeStream);

}