Callum Osborn
Callum Osborn

Reputation: 321

Play audio from API response JavaScript

I am trying to play a downloaded audio file and play it back in the browser from an external API.

I have a local API which is querying an external API where the file is located. My API method to get the file from the external API is:

public async Task<HttpResponseMessage> GetAudioAsync(string id)
{
    using (var httpClientHandler = new HttpClientHandler())
    {
        httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
        httpClientHandler.UseDefaultCredentials = true;

        var url = "externalapi/"

        using (var client = new HttpClient(httpClientHandler))
        {
            var result = await client.GetAsync(url);

            var response = new HttpResponseMessage(HttpStatusCode.OK);

            var bytes = await result.Content.ReadAsByteArrayAsync();

            response.Content = new ByteArrayContent(bytes);

            response.Content.Headers.ContentLength = bytes.LongLength;

            response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
            response.Content.Headers.ContentDisposition.FileName = "audio.wav";
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("audio/wav");

            return response;
        }                    
    }
}

I know that in Javascript I can set the source attribute of an audio element to playback audio. How do I take the data response from this API method and play the audio?

I've tried the following code below but I get console errors.

$.ajax({
  url: 'api/' + id,
  cache: false,
  type: 'GET'
}).done(function (data) {
  // var blob = new Blob([data], {type: 'audio/wav'});
  // Creating a blob instance removes the error but the audio is not correctly loaded to the audio element. I get no playback.      
  document.getElementById('filename').setAttribute('src', window.URL.createObjectURL(data));
}).fail(function(error) {
  console.log(error);
});

The error I get back from the browser is: ERROR TypeError: Failed to construct 'Blob': Iterator getter is not callable.

The response I get back from my local API is:

{
    "version": {
        "major": 1,
        "minor": 1,
        "build": -1,
        "revision": -1,
        "majorRevision": -1,
        "minorRevision": -1
    },
    "content": {
        "headers": [
            {
                "key": "Content-Length",
                "value": [
                    "0"
                ]
            },
            {
                "key": "Content-Disposition",
                "value": [
                    "attachment; filename=audio.wav"
                ]
            },
            {
                "key": "Content-Type",
                "value": [
                    "application/octet-stream"
                ]
            }
        ]
    },
    "statusCode": 200,
    "reasonPhrase": "OK",
    "headers": [],
    "requestMessage": null,
    "isSuccessStatusCode": true
}

UPDATE:

I think the issue may be that when I console.log(data) I get back an object rather than an array of bytes. That can be used to initialize a Blob object.

Upvotes: 3

Views: 4703

Answers (1)

Tjaart van der Walt
Tjaart van der Walt

Reputation: 5179

You can set the audio elements src attribute to point directly to the ASP.NET Web API endpoint:

<audio src="http://localhost:8000/api/endpoint/callId"></audio>

If you need to set it dynamically you can do it via javascript:

function setAudioElementSource(id)
{ 
  document.getElementById('yourAudioElement').setAttribute('src', 'http://localhost:8000/api/endpoint/callId')
}

Where endpoint is the name of your Controller and callId is the id of your call.

Here is an example of my action returning the call:

[HttpGet]
public async Task<HttpResponseMessage> GetAudioAsync(
    string id)
{
    var url = "http://localhost:8000/api/ExternalApi";

    using (var httpClientHandler = new HttpClientHandler())
    {
        httpClientHandler.UseDefaultCredentials = true;                

        using (var client = new HttpClient(httpClientHandler))
        {
            var result = await client.GetAsync(url);

            var response = new HttpResponseMessage(HttpStatusCode.OK);

            var bytes = await result.Content.ReadAsByteArrayAsync();

            response.Content = new ByteArrayContent(bytes);

            response.Content.Headers.ContentLength = bytes.LongLength;

            response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
            response.Content.Headers.ContentDisposition.FileName = "audio.wav";
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("audio/wav");

            return response;
        }
    }
}

Here is the response captured in Fiddler:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: audio/wav
Expires: -1
Server: Microsoft-IIS/10.0
Content-Disposition: attachment; filename=audio.wav
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?YzpcdXNlcnNcdGphYXJ0LnZhbmRlcndhbHRcZG9jdW1lbnRzXHZpc3VhbCBzdHVkaW8gMjAxNVxQcm9qZWN0c1xXZWJBcHBsaWNhdGlvbjFcV2ViQXBwbGljYXRpb24xXGFwaVxkb3dubG9hZFwxMjM=?=
X-Powered-By: ASP.NET
Date: Mon, 20 Aug 2018 09:12:45 GMT
Content-Length: 0

Content-Length is 0 as I am returning a blank byte array in my ExternalApi method, your Content-Length header should have a value greater than 0.

If you are not getting any errors, please try a different browser, some browsers do not support playback of .wav files


Side note:

You can also create a custom class FileResponse that inherits from HttpResponseMessage

public class FileResponse
     : HttpResponseMessage
{
    public FileResponse(
          byte[] fileContent
        , string mediaType
        , string fileName)
    {
        StatusCode = System.Net.HttpStatusCode.OK;
        Content = new StreamContent(new MemoryStream(fileContent));
        Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(mediaType);
        Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") { FileName = fileName };
    }
}

Usage:

[HttpGet]
public async Task<HttpResponseMessage> GetAudioAsync(
    string id)
{
    var url = "http://localhost:8000/api/ExternalApi";

    using (var httpClientHandler = new HttpClientHandler())
    {
        httpClientHandler.UseDefaultCredentials = true;                

        using (var client = new HttpClient(httpClientHandler))
        {
            var result = await client.GetAsync(url);                    

            var bytes = await result.Content.ReadAsByteArrayAsync();
            return new FileResponse(bytes, "audio/wav", "your-file-name.wav");
        }
    }
}

Upvotes: 1

Related Questions