【问题标题】:XHR response null retrieving resources from HTML5 Application Cache in iOSXHR 响应 null 从 iOS 中的 HTML5 应用程序缓存中检索资源
【发布时间】:2013-11-19 12:11:29
【问题描述】:

背景:

我正在尝试设置一个网页以在特定活动中用作 iPad2 上的信息亭显示(时间和预算排除了原生应用程序的可能性)。

该页面应该允许用户浏览一些不同的声音文件并查看它们的可视化。

我有一个缓存清单文件,其中列出了页面所需的所有音频文件。这导致第一次访问页面时需要很长时间的下载;这很好,但我希望这意味着资源在需要时无论网络状态如何都可以轻松访问。

我不介意页面在首次访问时加载缓慢;在这种情况下,我们的想法是公众不必见证这一点。

顺便说一句: 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();

还有一个看起来像这样的应用程序缓存清单:

CACHE MANIFEST
# 2013-11-19T01:24:07.338Z
foo.mp3

NETWORK:
*

在大多数环境中,代码大部分时间都可以正常工作。我一直同意任何浏览器提示在本地存储大量数据。

但是,我发现在 iOS 设备上,当我收到 XMLHttpRequest.onload 回调时,我遇到了状态码正常的情况,但如代码中所示,response === null

查看 Safari Web Inspector,我看到音频资源的条目以数百字节为单位,而不是每个字节数。

因此,我非常感谢有关我的方法的反馈以及关于我的缓存机制为何失败的具体答案。

【问题讨论】:

    标签: javascript ios html audio browser-cache


    【解决方案1】:

    首先,您的方法和代码非常好。但是,有第三方不希望您编写这样的代码,而希望您编写本机代码。此第三方是 Apple。

    每当您从应用程序缓存请求具有音频文件头的资源时,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/

    【讨论】:

    • 有意思,谢谢回复。我可能已经猜到这是苹果主义。没有尝试过解决方案,但如果我处于类似的位置,我会记住它。
    • FWIW,它也不会阻止我们最终使用的视频文件。
    猜你喜欢
    • 2011-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-17
    • 2011-09-09
    • 2016-02-27
    • 2011-12-28
    • 1970-01-01
    相关资源
    最近更新 更多