我正在努力寻找一个解决方案,使一个网络应用程序能够在iOS Safari上运行(例如在iPad、iPad2和iPhone4上):
这是我前段时间写的一个网络应用程序,允许用户搜索和收听短音乐样本(MP3格式,大小约为100KB至1.5MB)。音频播放器是基于Flash的,因此目前无法在iOS设备上使用,我必须使用HTML 5或者“直接”QuickTime对象来实现替代方案。
到目前为止,我的HTML 5和QuickTime在iOS设备上的替代方案都很好用,但我遇到了一个主要问题:与Flash和大多数支持HTML 5的Windows浏览器不同,在我的iPad 2上Safari无法在加载和播放后将音频文件存储在浏览器缓存中-无论是使用HTML5音频标记还是QuickTime对象。每次我从服务器加载音频文件进行播放(通过JavaScript命令,因此不需要更改或重新加载整个页面)时,它都会被完全下载。
如果用户先听取样品A,然后再听取样品B,Safari会忘记已经播放过样品A,并且如果我想再次收听它,则会再次下载整个MP3文件。在具有潜在窄带宽的移动设备上,这种行为是不能接受的。
是否有一种方法可以将HTML 5或QuickTime打开的已下载音频文件存储在Safari的缓存中,使其记住已经下载过它们-就像缓存HTML、CSS或JPEG图像等常规“web文件”一样,或者像Flash将这些对象存储在其本地缓存中?
我的第一次尝试是尝试使用带有清单文件的应用程序缓存-虽然这不是我的应用程序的真正目的...我没有一个静态的文件集,我想要缓存或“离线可用”-我只想缓存用户已经播放过的MP3文件。
应该可以使用“动态清单”:通过Apache PHP模块解析并从PHP会话中列出到目前为止已播放的文件-类似于下面的方式:
session_start();
header("Content-Type: text/cache-manifest, charset=UTF-8");
echo "CACHE MANIFEST\n";
foreach($_SESSION['playedSongs'] as $song)
{
echo $song."\n";
}
所以每当一首歌曲被加载/播放时,我可以通过AJAX访问PHP会话,插入已播放文件的文件名并通过调用window.applicationCache.update()或.swapCache()手动更新清单。
这样做存在两个问题:
首先:它不起作用。而且我甚至没有尝试使用动态清单:
<!DOCTYPE html>
<html manifest="cache.manifest">
<head>
<title>Test</title>
<script type="text/javascript"></script>
</head>
<body>
<div id="audio"></div><br />
<br />
<input type="button" value="playStuff(1)" onclick="playStuff(1)" />
<input type="button" value="playStuff(2)" onclick="playStuff(2)" />
</body>
</html>
缓存清单(cache.manifest)看起来像这样:
CACHE MANIFEST
song01.mp3
song02.mp3
通过添加,Apache正确返回为"text/cache-manifest"
AddType text/cache-manifest manifest
将此目录的 .htaccess 添加以下内容。
Apache 日志清楚地显示 Safari(或"AppleCoreMedia")在处理音频文件时并不关心应用程序缓存:
Safari 本身似乎确认了清单,确实预加载了这些文件:
192.168.0.40 - - [23/Jul/2011:12:45:46 +0200] "GET /websql/index2.html HTTP/1.1" 200 2619 "-" "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; de-de) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5"
192.168.0.40 - - [23/Jul/2011:12:45:46 +0200] "GET /websql/cache.manifest HTTP/1.1" 200 79 "-" "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; de-de) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5"
192.168.0.40 - - [23/Jul/2011:12:45:46 +0200] "GET /websql/cache.manifest?%3E HTTP/1.1" 200 79 "-" "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; de-de) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5"
192.168.0.40 - - [23/Jul/2011:12:45:46 +0200] "GET /websql/song02.mp3 HTTP/1.1" 200 120525 "-" "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; de-de) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5"
192.168.0.40 - - [23/Jul/2011:12:45:46 +0200] "GET /websql/song01.mp3 HTTP/1.1" 200 120525 "-" "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; de-de) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5"
到目前为止,我只是在Safari中打开了我的测试应用程序。
播放song01.mp3:
192.168.0.40 - - [23/Jul/2011:12:47:25 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 2 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:47:25 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:47:25 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:47:25 +0200] "GET /websql/song01.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:47:25 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:47:29 +0200] "GET /websql/song01.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:47:29 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
播放歌曲 song2.mp3:
192.168.0.40 - - [23/Jul/2011:12:48:04 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 2 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:48:04 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:48:04 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:48:04 +0200] "GET /websql/song02.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:48:04 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:48:05 +0200] "GET /websql/song02.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:48:05 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
再次播放 song1.mp3:
192.168.0.40 - - [23/Jul/2011:12:48:38 +0200] "GET /websql/song01.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:48:38 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:48:38 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:48:38 +0200] "GET /websql/song01.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:48:38 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:48:40 +0200] "GET /websql/song01.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:48:40 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
再次播放song2.mp3:
192.168.0.40 - - [23/Jul/2011:12:49:12 +0200] "GET /websql/song02.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:49:12 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:49:12 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:49:12 +0200] "GET /websql/song02.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:49:12 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:49:13 +0200] "GET /websql/song02.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
192.168.0.40 - - [23/Jul/2011:12:49:13 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)"
播放文件时每次都要完整地重新下载。因此,“AppleCoreMedia”(无论这是什么,我想它可能是由HTML 5音频元素触发的QuickTime插件?)要么无法访问应用程序缓存,要么只是没有意识到其中的文件。因此,如果我现在将我的iPad切换到“飞行模式”,Safari将无法访问/加载/播放文件。
我还尝试使用QuickTime对象而不是HTML 5音频标记(据我所知,在Safari中,HTML 5音频和视频始终使用QuickTime?)并使用以下内容控制它:
document.movie1.SetURL('song02.mp3');
没有什么变化,就像使用HTML 5音频一样,加载/播放时会重新下载所有内容。
即使这样可以工作,仍然存在一个问题:
要正确实现这一点,我需要在播放之前在应用程序缓存中加载MP3文件。但是,在这样做时似乎无法显示“真正”的进度:从更新应用程序缓存后触发的ProgressEvent似乎不提供有关已加载数据和文件完整大小的任何信息。只是“文件1/2”等,而不是像音频元素一样的“真正”进度,我可以确定类似于:“已加载1.2 MB中的100 KB”。
所有其他的存储方法,如Web SQL/Web数据库或本地存储也都没有帮助:
我看不到将MP3数据放入本地存储或Web数据库中并再次取出以进行播放的任何方法。 HTML 5画布元素具有toDataURL()函数以生成Base64编码表示形式并用于存储-音频元素似乎没有这样的功能。
最后一种“有些糟糕”的方法是尝试手动加载通过AJAX和PHP组合的Base64编码-MP3:PHP脚本输出MP3文件的Base64表示形式,并由AJAX加载,因此我可以将Base64表示形式存储为本地存储或Web数据库:
$infile = 'song01.mp3';
$contents = file_get_contents($infile);
$base64 = base64_encode($contents);
$audio = 'data:audio/mp3;base64,'.$base64;
echo $audio;
我尝试使用返回的 AJAX responseText 作为 audio source 标签的 source 参数。令人惊讶的是,在我的 iPad 2 上,Safari 无法加载这个“文件”,尽管在 Windows 上的 Chrome 中可以正常工作。可能是 Safari / iOS 上 Base64 URI 的大小限制导致的?
而且,即使在 iOS / Safari 中有效,我也不知道如何从 AJAX 查询中确定真实的进度...
我最后想到的事情是,在加载歌曲时不替换音频或 source 标签,而是将它们留在 DOM 结构中,检查是否已经存在当加载一首歌曲时,并且如果还没有加载过歌曲就添加一个新的音频标签。但是这并不起作用......
如果动态添加多个播放器实例(无论是 HTML5 标记还是 QuickTime 对象),而不是“覆盖”它们,Safari 就会忘记之前加载的第一个 MP3 文件,只要您在 DOM 树中插入新的音频或 QuickTime 元素 — 即使您甚至不必加载 / 播放新实例中的任何内容!只要不播放或插入其他音频 / 媒体相关元素,重复播放而不完全重新加载文件就可以正常工作。顺便说一下:仅使用 JavaScript 中的 Audio 对象并将其“保存”到数组中也不起作用 / 不会使 Safari 缓存任何内容。
如果您处于低带宽的蜂窝网络中,这会产生大量不必要的流量并且需要很长时间!
我已经花了三天时间解决这个问题,但仍然没有找到解决方案...
有任何想法吗?
curl -v URL
并在此处发布头部信息? - Matej Balantič