传输编码:分块和MP3 / Lame

问题描述

我有一个PHP Web服务,它返回mp3 HTTP响应。它可以工作,但是当我在DevTools中打开Chrome的网络限制时,它仅返回部分响应:

        $stream_start1 = Psr7\stream_for(fopen('./sounds/ping.mp3','r'));
        $stream_start2 = Psr7\stream_for(fopen('./sounds/ping.mp3','r'));
        $stream_start3 = Psr7\stream_for(fopen('./sounds/ping.mp3','r'));
        $stream_start4 = Psr7\stream_for(fopen('./sounds/ping.mp3','r'));
        $stream_start5 = Psr7\stream_for(fopen('./sounds/ping.mp3','r'));
        $stream_start6 = Psr7\stream_for(fopen('./sounds/ping.mp3','r'));
        $stream_start7 = Psr7\stream_for(fopen('./sounds/ping.mp3','r'));
        $stream_start8 = Psr7\stream_for(fopen('./sounds/ping.mp3','r'));
        $stream_start9 = Psr7\stream_for(fopen('./sounds/ping.mp3','r'));
        $stream_start10 = Psr7\stream_for(fopen('./sounds/ping.mp3','r'));
        $stream_start11 = Psr7\stream_for(fopen('./sounds/ping.mp3','r'));
        $stream_start12 = Psr7\stream_for(fopen('./sounds/ping.mp3','r'));
        $stream_start13 = Psr7\stream_for(fopen('./sounds/ping.mp3','r'));

        return new Psr7\AppendStream([$stream_start1,$stream_start2,$stream_start3,$stream_start4,$stream_start5,$stream_start6,$stream_start7,$stream_start8,$stream_start9,$stream_start10,$stream_start11,$stream_start12,$stream_start13]);

使用上面的代码,并限制了开发人员,我得到了7个ping而不是13个ping。

实际上,实际代码是从第三方服务获取流并将其夹在两个文件之间:

 return new Psr7\AppendStream([file1Stream,3rdPartyStream,file2Stream])

因此,我不一定知道要设置Content-Length的长度,因此它使用的是transfer-encoding:分块。

当我在开发工具中查看请求的缩短响应时,我始终看到它们以一些LAME元数据或增加的静结尾:

e¨Deš•†š¹‡ÌC`̹3†'2 CBR9S¨Âöe­i´g¡ÿ–W©^¥ÔzWI}I8¸¶vv,¸¼Ù£J*ÙÙû±W•'X&+X±£@È ·bbÂìýbŸâÿâŸëLAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿóbÄ”AIÖtUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU

我想知道的是,由于接收块的延迟,浏览器是否有可能在块序列的末尾混淆一些此LAME信息(或其他信息)。是否有可能在音频二进制文件中混有类似于“空块”的内容,表示最后一个块?有没有一种方法可以“转义”或编码数据以避免像这样的潜在序列?通过压缩,也许?

我的另一种选择是在转发前将第3方流(在三明治中间)完全读取到内存中。这样,我可以计算长度并设置Content-Length,所以就是计划B。

更新: 我曾尝试在我的13个文件示例中设置Content-Length,但仍然得到7 ping作为响应。我计算出的长度:

$length = strlen($stream_start0->getContents()) * 13 

哪个是122252。但是返回的文件内容长约65529字节(十六进制编辑器说65536 ...?)。无论如何,它太短了。另外,transfer-encoding标头不见了,这意味着我想它不再要分块了?

UPDATE2: 还有可能的问题是我正在串联mp3文件原始文件。我认为从技术上讲,当您这样做时,事情会变得很阴暗,尤其是如果音频包含ID3标签等,尽管在上面使用的ping文件中,不应有任何ID3标签。但是,在我们没有开发工具的情况下,我的stg环境中,临界值并非总是发生在文件间的中断处。

解决方法

因此,以上所有内容都是红色鲱鱼。原来,问题出在我用JavaScript代码复制了一篇博客文章,而该博客文章仅调用一次response.getReader().read(),因此仅获得了响应的第一部分。

显然,它打算像大多数流API一样,在循环中调用直到到达流末尾,但我错过了。

解决方案使用response.blob(),它读取到流的末尾并一次性创建我需要的Blob。

我以为我已经验证了我复制的所有内容,但我想没有。但是我学到了一些东西,就是这样。

Docs on reader.read()
Docs on response.blob()