通过 Ajax 从 Rails 控制器播放音频 (mp3)

Play Audio (mp3) from Rails Controller via Ajax

我们想要从 ajax 调用生成音频文件,return 来自该文件的数据,并让 Javascript 在页面上播放音频。 但是我们得到

Uncaught (in promise) DOMException: Failed to load because no supported source was found.
# in the controller
def preview
    file_name = Tempfile.new(["preview", '.mp3'], binmode: true)
  # fill file with data here

# after writing out file

    Rails.logger.debug Mp3Info.open(file_name.path)
    soundfile = File.open(file_name.path, 'r')
    binary = soundfile.read
  
    send_data binary, filename: 'preview.mp3', type: 'audio/mp3', disposition: 'inline'
end

在页面上

<form id='test-gallery'>
<%= text_field_tag 'input-field-thing' %>
</form>

<script>
        $( "#control-thing" ).on( "click", function() {       
          var identifyingStuff = $("#input-field-thing").val();
          console.log(identifyingStuff);

          Rails.ajax({
            type: "post",
            url: "/sound_gallery/preview.js",
            data: Rails.serializeElement(Rails.$("form")[0]),
            success: (response) => {
              console.log(response);

              var blob = new Blob([response], { type: 'audio/mp3' });
              console.log(blob);

              var url = window.URL.createObjectURL(blob); //where value is the blob
              console.log(url);
              var audio = new Audio();
              audio.src = url;
              audio.play();
            },
          });
        });
</script>

前端的console.log(response)写出了一个看似二进制的文件。看起来像这样(我不得不使用图像,因为粘贴导致无法保存!):

然后 console.log(blob) 写了这个

Blob {size: 17989, type: "audio/mpeg"}

我们让我相信数据正在正确发送,但实际上在这一点上我不知道。

浏览器的 'Network' 面板在播放 blob 时显示 Status: 206。那将是浏览器错误的根源,但问题仍然存在 'why?'.

我找到了一个解决方案并把它留在这里以防我可以节省其他人几天的工作时间。

我不需要在服务器端进行任何更改。 send_data 工作正常。

在前端,我无法使用 Rails.ajax 让它工作。 下面是一个使用 XMLHttpRequest

的解决方案
<form id='test-gallery'>
<%= text_field_tag 'input-field-thing' %>
</form>
<span id='control-thing1'>XMLHttpRequest</span>

<script>
        $( "#control-thing1" ).on( "click", function(e) {
          let token = document.getElementsByName("csrf-token")[0].content;
          e.preventDefault();
          let inputText = $('#input-field-thing').val();
          let data = JSON.stringify({'input': inputText});

          let xmlHttpRequest = new XMLHttpRequest(); 
          xmlHttpRequest.open("POST", "/sound_gallery/preview.js");
          xmlHttpRequest.responseType = "blob"; // <-- this is what can't be set in Rails.ajax
          xmlHttpRequest.setRequestHeader("Accept", "application/json");
          xmlHttpRequest.setRequestHeader("Content-Type", "application/json");
          xmlHttpRequest.setRequestHeader("X-CSRF-Token", token); 
          xmlHttpRequest.onreadystatechange = function() {
            if (this.readyState == 4 && this.status == 200) {
              var url = window.URL.createObjectURL(this.response);
              var audio = new Audio();
              audio.src = url;
              audio.play();
            }
          };

          xmlHttpRequest.send(data); 

       });

</script>