背景:
我正在尝试设置一个网页以在特定事件中用作 iPad2 上的信息亭显示(时间和预算排除了 native 应用程序的可能性)。
该页面应该允许用户浏览一些不同的声音文件并查看它们的可视化效果。
我有一个缓存 list 文件,其中列出了页面所需的所有音频文件。导致首次访问页面时下载时间较长;这很好,但我希望这意味着无论网络状态如何,在需要时都可以轻松访问这些资源。
我不介意首次访问时页面加载缓慢;在这种情况下,我们的想法是公众不必目睹这一点。
顺便说一下:作为 createMediaElementSource()
显然在 iOS WebKit 中不起作用,这意味着要让 WebAudio 处理我的音频,我必须使用 createBufferSource()
来自由 XMLHTTPRequest
的结果构造的缓冲区.这意味着我无法以 <audio>
的形式进行流式传输元素将允许,并且必须等待整个请求,以便我可以设置缓冲区并开始播放一些声音。我还必须非常小心过多的内存使用会导致浏览器崩溃,所以我不能保留几个现成的缓冲区。
在经历了很多挫折之后,我正在考虑不涉及 WebAudio 的替代方案;这似乎意味着我可以流式传输音频,并且希望在切换音轨时不会遇到同样的延迟或由于内存不足而不断崩溃。
我目前的问题:
我有onclick
做这样的事情的事件:
var request = new XMLHttpRequest();
request.open('GET', "foo.mp3", true);
request.onload = function(e) {
if (e.target.status === 200) {
if (e.target.response === null) {
alert('Empty response for foo.mp3'); //This is the case I don't want
return;
}
var buf = ctx.createBuffer(e.target.response false);
var sourceNode = ctx.createBufferSource();
sourceNode.buffer = buf;
sourceNode.connect(analyser);
// etc.
}
};
request.send();
和一个看起来像这样的应用程序缓存 list :
CACHE MANIFEST
# 2013-11-19T01:24:07.338Z
foo.mp3
NETWORK:
*
在大多数环境中,代码大部分时间都按预期工作。我一直同意任何浏览器提示在本地存储大量数据。
但是,当我收到 XMLHttpRequest.onload
时,我在 iOS 设备上发现了这一点回调,我遇到了状态代码正常的情况,但如代码所示,response === null
.
查看 Safari Web Inspector,我看到音频资源的条目有数百个字节,而不是每个条目几兆字节。
因此,我非常感谢对我的方法的反馈以及关于为什么我的缓存机制失败的具体答案。
最佳答案
首先,您的方法和代码非常好。但是,有第三方不希望您像那样编写代码,而希望您编写 native 代码。这个第三方就是 Apple。
每当您从具有音频文件 header 的应用程序缓存请求资源时,iOS 上的 Safari 将返回空结果。您可以以 blob、arraybuffer 甚至纯文本的形式请求数据,尽管如此,文件仍将被阻止。它只会阻止音频文件,而不会阻止以相同方式存储的图像文件。重命名无济于事,因为文件头本身会被解析以阻止不需要的内容。
您的方法只需稍作修改即可使用。如果您在服务器上对数据进行编码,将其编码存储在应用程序缓存中,并在加载文件后即时解码,iOS 将无法检测到您要加载音频文件。伪装文件头很重要。
一种方法是在服务器上以 base64 编码您的音频文件(选择您自己的工具)。您的加载函数将保持几乎相同:
var request = new XMLHttpRequest();
request.open('GET', "foo.mp3", true);
request.onload = function(e) {
if (e.target.status === 200) {
if (e.target.response === null) {
alert('Empty response for foo.mp3'); //This case won't be reached anymore
return;
}
var decodedFile = decodeFile(e.target.response);
var buf = ctx.createBuffer(e.target.response false);
var sourceNode = ctx.createBufferSource();
sourceNode.buffer = buf;
sourceNode.connect(analyser);
// etc.
}
};
request.send();
base64 的解码函数可以这样写:
function decodeFile(encodedData) {
var arrayBufferLength = (encodedData.length / 4) * 3;
var bytesAsString = atob(encodedData);
var resultArray = new Uint8Array(new ArrayBuffer(arrayBufferLength));
for (var i = 0; i < arrayBufferLength; i++) {
resultArray[i] = bytesAsString.charCodeAt(i);
}
return resultArray.buffer;
}
这篇博文详细描述了这个问题:http://html5doctor.com/taking-web-audio-offline-in-ios-6-safari/
关于javascript - XHR 响应 null 从 iOS 中的 HTML5 应用程序缓存中检索资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20071246/