处理不缓存音频的 IOS Web 浏览器

koj*_*ow7 5 javascript safari audio ios reactjs

我有一个语言网站,我正在致力于教授语言。用户可以单击对象并听到他们单击的内容的音频。许多将使用它的人位于互联网连接速度较慢的偏远地区。因此,我需要在加载每个活动之前缓存音频,否则会有太多延迟。

以前,我遇到了预加载不起作用的问题,因为 iOS 设备不允许在没有单击事件的情况下加载音频。我已经解决了这个问题,但是,我现在有另一个问题。iOS/Safari只允许加载最新的音频文件。因此,每当用户点击另一个音频文件(即使之前点击过),它不会被缓存,浏览器必须再次下载它。

到目前为止,我还没有找到合适的解决方案。2011~2012 年左右有很多帖子试图解决这个问题,但我还没有找到好的解决方案。一种解决方案是将活动的所有音频剪辑合并到一个音频文件中。这样,每个活动只会将一个音频文件加载到内存中,然后您只需选择音频文件的特定部分即可播放。虽然这可能有效,但每当需要更改、添加或删除音频剪辑时,它也会变得令人讨厌。

我需要在ReactJS/Redux环境中运行良好并在 iOS 设备上正确缓存的东西。

是否有 2020 年有效的解决方案?

Ahm*_*tar 6

您可以使用IndexedDB. 它是用于客户端存储大量结构化数据(包括文件/blob)的低级 API。IndexedDBAPI 很强大,但对于简单的情况来说似乎太复杂了。如果你喜欢一个简单的API,尽量库,比如localForagedexie.js

localForage 是一个 Polyfill,为客户端数据存储提供简单的 name:value 语法,它在后台使用 IndexedDB,但在不支持 IndexedDB 的浏览器中回退到 WebSQL,然后使用 localStorage。

您可以在IndexedDB此处查看浏览器支持:https : //caniuse.com/#search=IndexedDB。它得到了很好的支持。这是我用来展示这个概念的一个简单例子:

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Audio</title>
</head>

<body>
  <h1>Audio</h1>

  <div id="container"></div>

  <script src="localForage.js"></script>
  <script src="main.js"></script>
</body>

</html>
Run Code Online (Sandbox Code Playgroud)

main.js

"use strict";

(function() {
  localforage.setItem("test", "working");

  // create HTML5 audio player
  function createAudioPlayer(audio) {
    const audioEl = document.createElement("audio");
    const audioSrc = document.createElement("source");
    const container = document.getElementById("container");
    audioEl.controls = true;
    audioSrc.type = audio.type;
    audioSrc.src = URL.createObjectURL(audio);
    container.append(audioEl);
    audioEl.append(audioSrc);
  }

  window.addEventListener("load", e => {
    console.log("page loaded");
    // get the audio from indexedDB
    localforage.getItem("audio").then(audio => {
      // it may be null if it doesn't exist
      if (audio) {
        console.log("audio exist");
        createAudioPlayer(audio);
      } else {
        console.log("audio doesn't exist");
        // fetch local audio file from my disk
        fetch("panumoon_-_sidebyside_2.mp3")
          // convert it to blob
          .then(res => res.blob())
          .then(audio => {
            // save the blob to indexedDB
            localforage
              .setItem("audio", audio)
              // create HTML5 audio player
              .then(audio => createAudioPlayer(audio));
          });
      }
    });
  });
})();
Run Code Online (Sandbox Code Playgroud)

localForage.js只包含这里的代码:https : //github.com/localForage/localForage/blob/master/dist/localforage.js

您可以检查IndexedDBchrome 开发工具,您会在那里找到我们的项目: IndexedDB_Blob 如果你刷新页面,你仍然会在那里看到它,你也会看到创建的音频播放器。我希望这回答了你的问题。

顺便说一句,如果仍然可以存储音频文件,那么旧版本的 safari IOS 不支持存储blobIndexedDB因为ArrayBuffer它得到了很好的支持。这是一个使用示例ArrayBuffer

main.js

"use strict";

(function() {
  localforage.setItem("test", "working");

  // convert arrayBuffer to Blob
  function arrayBufferToBlob(buffer, type) {
    return new Blob([buffer], { type: type });
  }

  // convert Blob to arrayBuffer
  function blobToArrayBuffer(blob) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.addEventListener("loadend", e => {
        resolve(reader.result);
      });
      reader.addEventListener("error", reject);
      reader.readAsArrayBuffer(blob);
    });
  }

  // create HTML5 audio player
  function createAudioPlayer(audio) {
    // if it's a buffer
    if (audio.buffer) {
      // convert it to blob
      audio = arrayBufferToBlob(audio.buffer, audio.type);
    }
    const audioEl = document.createElement("audio");
    const audioSrc = document.createElement("source");
    const container = document.getElementById("container");
    audioEl.controls = true;
    audioSrc.type = audio.type;
    audioSrc.src = URL.createObjectURL(audio);
    container.append(audioEl);
    audioEl.append(audioSrc);
  }

  window.addEventListener("load", e => {
    console.log("page loaded");
    // get the audio from indexedDB
    localforage.getItem("audio").then(audio => {
      // it may be null if it doesn't exist
      if (audio) {
        console.log("audio exist");
        createAudioPlayer(audio);
      } else {
        console.log("audio doesn't exist");
        // fetch local audio file from my disk
        fetch("panumoon_-_sidebyside_2.mp3")
          // convert it to blob
          .then(res => res.blob())
          .then(blob => {
            const type = blob.type;
            blobToArrayBuffer(blob).then(buffer => {
              // save the buffer and type to indexedDB
              // the type is needed to convet the buffer back to blob
              localforage
                .setItem("audio", { buffer, type })
                // create HTML5 audio player
                .then(audio => createAudioPlayer(audio));
            });
          });
      }
    });
  });
})();
Run Code Online (Sandbox Code Playgroud)

IndexedDB_ArrayBuffer