繁体   English   中英

如何将 blob URL 转换为音频文件并将其保存到服务器

[英]How to convert a blob URL to a audio file and save it to the server

我已经成功录制并添加了录制的音频并将其放置在我的 HTML 页面的音频标签中。

<audio controls="" src="blob:https://localhost:3000/494f62b9-0513-4d1c-9206-6569083a2661"></audio>

此外,我已经使用这一行成功地从源标记中获取了 blob 源 url。 var source = document.getElementById("Audio").src; 这是我的 blob 网址

blob: https://localhost:3000/494f62b9-0513-4d1c-9206-6569083a2661

现在如何将 blob 源 url 转换为音频文件并将其发送到我的服务器。 我已经尝试了很多,帮助做到这一点,我是这个 blob 的新手。 由于我使用这个记录器 api 在所有浏览器上工作,我只有这个机会通过获取 blob 源然后将其转换为音频文件并使用表单数据将音频文件发送到我的服务器。

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

<head>
  <meta charset="UTF-8">
  <title>FeedBack URL</title>
  <link rel="shortcut icon" href="./favicon.ico">
  <meta content="width=device-width" name="viewport">
  <meta name="theme-color" content="#00e5d2">
  <style>
    * {
      padding: 0;
      margin: 0
    }

    a {
      color: #009387;
      text-decoration: none
    }

    a:visited {
      color: #930087
    }

    body {
      margin: 1rem;
      font-family: sans-serif
    }

    main {
      max-width: 28rem;
      margin: 0 auto;
      position: relative
    }

    #controls {
      display: flex;
      margin-top: 2rem
    }

    button {
      flex-grow: 1;
      height: 2.5rem;
      min-width: 2rem;
      border: none;
      border-radius: .15rem;
      background: blue;
      margin-left: 2px;
      box-shadow: inset 0 -.15rem 0 rgba(0, 0, 0, .2);
      cursor: pointer;
      display: flex;
      justify-content: center;
      align-items: center
    }

    button:focus,
    button:hover {
      outline: none;
      background: blue;
    }

    button::-moz-focus-inner {
      border: 0
    }

    button:active {
      box-shadow: inset 0 1px 0 rgba(0, 0, 0, .2);
      line-height: 3rem
    }

    button:disabled {
      pointer-events: none;
      background: #d3d3d3
    }

    button:first-child {
      margin-left: 0
    }

    button svg {
      transform: translateY(-.05rem);
      fill: #000;
      width: 1.4rem
    }

    button:active svg {
      transform: translateY(0)
    }

    button:disabled svg {
      fill: #9a9a9a
    }

    button text {
      fill: #00e5d2
    }

    button:focus text,
    button:hover text {
      fill: #00ffe9
    }

    button:disabled text {
      fill: #d3d3d3
    }

    #formats,
    #mode {
      margin-top: .5rem;
      font-size: 80%
    }

    #mode {
      float: right
    }

    #support {
      display: none;
      margin-top: 2rem;
      color: red;
      font-weight: 700
    }

    #list {
      margin-top: 1.6rem
    }

    audio {
      display: block;
      width: 100%;
      margin-top: .2rem
    }

    li {
      list-style: none;
      margin-bottom: 1rem
    }

    .popup-position {
      display: none;
      position: fixed;
      top: 0;
      left: 0;
      background-color: rgba(0, 0, 0, 0.7);
      width: 100%;
      height: 100%;

      /* // The Modal Wrapper */
    }

    #popup-wrapper {
      text-align: left;
    }

    /* //The Modal Container */
    #popup-container {

      background-color: #fff;
      padding: 20px;
      border-radius: 10px;
      width: 300px;
      margin: 70px auto;
    }

    #closePopup {
      margin-left: 281px;
      margin-top: -18px;
    }
  </style>
</head>

<body>
  <a href="javascript:void(0)" onclick="toggle_visibility('contact-popup');">Open Popup</a>
  <div class="popup-position" id="contact-popup">
    <div class="popup-wrapper">
      <div id="popup-container">
        <h5>Feedback</h5>
        <p id="closePopup"><a href="javascript:void(0)" style="color: red;" title="Close"
            onclick="toggle_visibility('contact-popup');">X</a></p>
        <main>
          <div id="controls">
            <button id="record" disabled="" autocomplete="off" title="Record">
              <svg viewBox="0 0 100 100" id="recordButton">
                <circle cx="50" cy="50" r="46"></circle>
              </svg>
            </button>
            <button id="pause" disabled="" autocomplete="off" title="Pause">
              <svg viewBox="0 0 100 100">
                <rect x="14" y="10" width="25" height="80"></rect>
                <rect x="62" y="10" width="25" height="80"></rect>
              </svg>
            </button><button id="resume" disabled="" autocomplete="off" title="Resume">
              <svg viewBox="0 0 100 100">
                <polygon points="10,10 90,50 10,90"></polygon>
              </svg>
            </button><button id="stop" autocomplete="off" disabled="" title="Stop">
              <svg viewBox="0 0 100 100">
                <rect x="12" y="12" width="76" height="76"></rect>
              </svg>
            </button>
          </div>
          <div id="mode">
            Native support,<a href="?polyfill">force polyfill</a>
          </div>
          <div id="formats"></div>
          <div id="support">
            Your browser doesn’t support MediaRecorder
            So please use chrome or edge or mozilla
          </div>
          <ul id="list"></ul>
          <form enctype="multipart/form-data"></form>
            <input id="image-file" type="file" hidden />
            <button type="button" id="formSubmit" onclick="sendto();">Submit</button>
          </form>
        </main>
        <div class="modal-footer">
        </div>
      </div>
    </div>
  </div>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
  <script>
    (function () {
      var a, i, b, d, f, g, l = ["start", "stop", "pause", "resume"],
        m = ["audio/webm", "audio/ogg", "audio/wav"],
        j = 1024,
        k = 1 << 20;

      function n(e) {
        var r, $ = Math.abs(e);
        return $ >= k ? (r = "MB", e /= k) : $ >= j ? (r = "KB", e /= j) : r = "B", e.toFixed(0).replace(
          /(?:\.0*|(\.[^0]+)0+)$/, "$1") + " " + r;
      }

      function e(e) {
        i.innerHTML = "", navigator.mediaDevices.getUserMedia({
          audio: !0
        }).then(function (r) {
          a = new MediaRecorder(r), l.forEach(function (e) {
            a.addEventListener(e, t.bind(null, e));
          }), a.addEventListener("dataavailable", s), "full" === e ? a.start() : a.start(1e3);
        }), b.blur(), setTimeout(myFunction, 16000);
      }

      function o() {
        a.stop(), a.stream.getTracks()[0].stop(), g.blur();
      }

      function p() {
        a.pause(), d.blur();
      }

      function q() {
        a.resume(), f.blur();
      }

      function s(e) {
        var r = document.createElement("li"),
          $ = document.createElement("strong");
        $.innerText = "dataavailable: ", r.appendChild($);
        var a = document.createElement("span");
        a.innerText = e.data.type + ", " + n(e.data.size), r.appendChild(a), a.setAttribute("id", "span");
        var o = document.createElement("audio");
        o.controls = !0, o.src = URL.createObjectURL(e.data), o.setAttribute("id", "Audio"), r.appendChild(o), i
          .appendChild(r);

      }

      function t(e) {

        var r = document.createElement("li");
        r.innerHTML = "<strong>" + e + ": </strong>" + a.state, "start" === e && (r.innerHTML += ", " + a
            .mimeType), i.appendChild(r), "recording" === a.state ? (b.disabled = !0,
            f.disabled = !0, d.disabled = !1, g.disabled = !1) : "paused" === a.state ? (b
            .disabled = !0, f.disabled = !1, d.disabled = !0, g.disabled = !1) : "inactive" === a
          .state && (b.disabled = !1, f.disabled = !0, d.disabled = !0, g
            .disabled = !0);
      }
      i = document.getElementById("list"),
        b = document.getElementById("record"),
        f = document.getElementById("resume"),
        d = document.getElementById("pause"),
        g = document.getElementById("stop"),
        MediaRecorder.notSupported ? (i.style.display = "none",
          document.getElementById("controls").style.display = "none",
          document.getElementById("formats").style.display = "none",
          document.getElementById("mode").style.display = "none",
          document.getElementById("support").style.display = "block") : (document.getElementById("formats")
          .innerText = "Format: " + m
          .filter(function (e) {
            return MediaRecorder.isTypeSupported(e);
          }).join(", "), b.addEventListener("click", e.bind(null,
            "full")), f.addEventListener("click", q), d.addEventListener("click", p),
          g.addEventListener("click", o), b.disabled = !1);
    })();

    function myFunction() {
      document.getElementById("stop").click();
    }

    function toggle_visibility(id) {
      var element = document.getElementById(id);

      if (element.style.display == 'block')
        element.style.display = 'none';
      else
        element.style.display = 'block';
    }

    async function sendto() {
      var source = document.getElementById("Audio").src;



      $.ajax({
      type: 'POST',
      url: "http://localhost:3000/audioUpload",
      data: data,
      cache: false,
      processData: false,
      contentType: false,
      success: function(result) {

      }
    })


  </script>

</body>

</html>

我已经尝试了获取代码

let file = await fetch(source).then(r => r.blob()).then(blobFile => new File([blobFile], fileName, {
        type: res[0]
      })); 

但它给了我原始数据我不知道如何发送和接收原始数据。

首先,您需要一个合适的函数来发送数据。 您最初的fetch方法很接近,但并不完美。

让我们考虑下面的函数。 它在file参数中接受一个Blob 稍后将在答案中创建此Blob sendAudioFile函数中创建一个新的FormData对象。 Blob附加到 formData。

现在将带有POST方法的formData发送到您的服务器,并使用formDatabody属性。

const sendAudioFile = file => {
  const formData = new FormData();
  formData.append('audio-file', file);
  return fetch('http://localhost:3000/audioUpload', {
    method: 'POST',
    body: formData
  });
};

现在要创建文件,您需要捕获录制的流。 现在您直接将录音设置为您的音频元素,但这对您获取录制的数据没有用。

getUserMedia的回调中添加一个空数组,并将其称为data 这个数组将捕获所有记录的数据并使用它来创建一个Blob

dataavailable事件处理程序中,将e.data (这是记录的数据)推e.data data数组。

添加另一个侦听stop事件的事件侦听器。 每当记录停止并收集所有数据时,在stop事件回调中创建一个Blob 您可以指定文件的MIME type以说明其格式。

现在您拥有带有记录数据的Blob ,可以将其传递给sendAudioFile函数,该函数会将您的Blob发送到服务器。

navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
  // Collection for recorded data.
  let data = [];

  // Recorder instance using the stream.
  // Also set the stream as the src for the audio element.
  const recorder = new MediaRecorder(stream);
  audio.srcObject = stream;

  recorder.addEventListener('start', e => {
    // Empty the collection when starting recording.
    data.length = 0;
  });

  recorder.addEventListener('dataavailable', event => {
    // Push recorded data to collection.
    data.push(event.data);
  });

  // Create a Blob when recording has stopped.
  recorder.addEventListener('stop', () => {
    const blob = new Blob(data, { 
      'type': 'audio/mp3' 
    });
    sendAudioFile(blob);
  });

  // Start the recording.
  recorder.start();
});

添加到@Emiel Zuubier 很好的答案,如果您需要通过 base64 编码的数据 URI 发送数据。 在这种情况下;

blobToBase64(blob) {
        const reader = new FileReader();
        reader.readAsDataURL(blob);
        return new Promise(resolve => {
            reader.onloadend = () => {
                resolve(reader.result);
            };
        });
    };

然后像这样发送到服务器;

 blobToBase64(audioBlob)
        .then(base64Data => {
            const file = "data:audio/webm;base64," + base64Data;
            const formData = new FormData();
            formData.append('file', file);
            return fetch(url, {
                method: 'POST',
                body: formData
            }).then(res => res.json())
           })

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM