【问题标题】:Why does storing a stream of base64 data not work?为什么存储 base64 数据流不起作用?
【发布时间】:2019-01-13 18:03:35
【问题描述】:

使用a react native audio record library 我正在尝试录制一个 3 秒的 .wav 音频文件。在录制过程中,可以使用此“连接”/“功能”接收 base64 数据,每次从录制中接收到一大块数据时都会激活该功能(不确定您会如何称呼它):

AudioRecord.on('data', data => {
  // base64-encoded audio data chunks
});

我在按下按钮时激活的函数中执行此操作。当我尝试将收到的所有数据存储在这样的变量中时,就会出现问题:

var tempString = '';
AudioRecord.on('data', data => {
  tempString += data;
});

由于某种原因,当我在录制完成后(使用 settimeout)console.log tempString 时,它似乎只存储了第一次收到任何数据的数据。另外,当我创建一个变量计数时,每次接收数据时都会计数,它只是正常计数。

当我控制台记录数据时,它确实打印出所有数据。我尝试推送到一个数组并监听变量何时发生变化,但我尝试的所有操作都导致它只存储我收到的第一条数据。如何将收到的所有数据存储在变量中?这甚至可能吗?

【问题讨论】:

    标签: javascript react-native


    【解决方案1】:

    背景:Base64 填充

    Base64 中,每个输出字符代表输入的 6 位 (26 = 64)。编码数据时,第一步是将输入的位拆分为 6 位块。让我们以输入字符串“hello”为例(编码为 ASCII 或 UTF-8 的二进制)。如果我们尝试将其位拆分为 6 位块,我们将意识到它并没有平均划分:最后一个块只有 4 位。

    h         e         l         l         o
    01101000  01100101  01101100  01101100  01101111 
    011010 000110 010101 101100 011011 000110 1111?? 
    a      G      V      s      b      G      ?     
    

    我们可以用0s 填充输入流来填充缺失的位。

    011010 000110 010101 101100 011011 000110 111100
    a      G      V      s      b      G      8
    

    这给了我们"aGVsbG8",并且在 JavaScript 中的快速健全性测试证实了atob("aGVsbG8") === "hello"。还没有问题。

    如果我们自己解码这个块,这是可行的,因为我们知道一旦我们到达块的末尾,我们还没有解码的剩余两位必须是填充,并且可以被忽略。但是,如果这只是一个流的一部分,紧接着是更多的 base64 数据,我们就无法判断我们是在一个块的末尾!

    例如,让我们尝试将aGVsbG8 与自身连接,并将aGVsbG8aGVsbG8 解码为单个值。

    a      G      V      s      b      G      8      a      G      V      s      b      G      8     
    011010 000110 010101 101100 011011 000110 111100 011010 000110 010101 101100 011011 000110 111100
                                                  ||- padding that should be ignored
    01101000 01100101 01101100 01101100 01101111  00011010 00011001 01011011 00011011 00011011 1100????
    h        e        l        l        o         \x1A     \x19     [        \x1B     \x1B     ?
    

    两个填充位导致解码流错位,剩余数据被破坏。

    在这些情况下,标准解决方案是在编码数据后向两个= 填充字符添加零。每个= 代表六位填充数据。这些标记了编码值的结束,但它们也允许保持输入数据和输出数据之间的对齐:在流中使用适当的填充,每四个字符的编码数据块可以明确地解码为一到三个字节解码数据,无需单独了解数据对齐。我们的示例需要六位填充来保持对齐,给我们aGVsbG8=。如果我们将其与自身连接,我们可以看到现在解码成功:

    a      G      V      s      b      G      8      =      a      G      V      s      b      G      8       =    
    011010 000110 010101 101100 011011 000110 111100 PPPPPP 011010 000110 010101 101100 011011 000110 111100 PPPPPP
    01101000 01100101 01101100 01101100 01101111  00PPPPPP 01101000 01100101 01101100 01101100 01101111  00PPPPPP
    h        e        l        l        o         padding  h        e        l        l        o         padding  
    

    问题:无能力的解码器

    使用功能齐全的编码器和解码器,您的方法应该可以正常工作。每个块都应该包含适当的填充,解码器应该能够跳过它并组装正确的结果。

    很遗憾,很多最常见的 base64 解码库都不支持这一点。

    Node 的 Buffer 只是假设它正在获取单个编码值,因此当它看到填充(可能在第一个块的末尾)时,它假定它是值的末尾,并停止解码,丢弃其余的数据。

    > Buffer.from('aGVsbG8=', 'base64')
    <Buffer 68 65 6c 6c 6f>
    > Buffer.from('aGVsbG8=aGVsbG8=', 'base64')
    <Buffer 68 65 6c 6c 6f>
    

    浏览器的atob 会抛出错误,而不是默默地忽略数据:

    > atob("aGVsbG8=")
    "hello"
    > atob("aGVsbG8=aGVsbG8=")
    InvalidCharacterError: String contains an invalid character
    

    解决方案

    在填充时手动拆分

    如果我们保持您将所有数据存储在单个字符串中的方法,我们需要自己负责拆分填充。 (注意: 通常,重复附加到字符串可能会出现问题,因为如果 JavaScript 引擎无法对其进行优化,它会变得非常慢。在实践中这可能不是问题,但通常可以避免。)

    我们可以使用匹配一个或多个= 填充字符序列的正则表达式来做到这一点,

    const input = "aGVsbG8=aGVsbG8=aGVsbG8=aGVsbG8=";
    const delimiter = /=+/g;
    

    在上面分割字符串,

    const pieces = input.split(delimiter);
    

    单独解码片段,

    const decodedPieces = pieces.map(piece => Buffer.from(piece, 'base64'));
    

    然后在一个步骤中组合它们的输出(比逐步进行更有效)。

    const decoded = Buffer.concat(decodedPieces);
    console.log(decoded.toString('ascii'));
    
    'hellohellohellohello'
    

    单独存储块

    但是,在您的情况下,从头开始将块存储在数组中并完全跳过连接和拆分可能会更简单。

    const decodedPieces = [];
    AudioRecord.on('data', data => {
      decodedPieces.push(Buffer.from(data, 'base64'));
    });
    
    // later, when you need to collect the data...
    const decoded = Buffer.concat(decodedPieces);
    

    【讨论】:

      猜你喜欢
      • 2016-12-13
      • 2020-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-16
      • 1970-01-01
      • 2021-11-05
      相关资源
      最近更新 更多