javascriptasp.net-coreblobogg

save blob file (audio/ogg) using asp.net mvc core


must be a simple question but I've been struggling for a week with it. I have a super simple jquery based audio capture - what I just want is to save it as a file based on a controller action. The problem is that I can't figure out how to pass blob file to the controller. This is the code I have to capture audio (see below). With image I can just use

document.getElementById("canvas").toDataURL("image/png");

then pass it to controller and save it as image, something like this:

   using (FileStream fs = new FileStream(fileNameWitPath, FileMode.Create))
    {
        using (BinaryWriter bw = new BinaryWriter(fs))
        {
            byte[] data = Convert.FromBase64String(imageData);
            bw.Write(data);
            bw.Close();
        }
        fs.Close();
    }

so ideally I would want something akin to how I save images.

$(function () {
    $('body').append(
        $('<button/>')
            .attr("id", "start")
            .html("start")
    ).append(
        $('<button/>')
            .attr("id", "stop")
            .html("stop")
    ).append(
        $('<div/>').
            attr("id", "ul")
    )
 
    let log = console.log.bind(console),
        ul = $('#ul')[0],
        start = $('#start')[0],
        stop = $('#stop')[0],
        stream,
        recorder,
        counter = 1,
        chunks,
        media;
    media = {
        tag: 'audio',
        type: 'audio/ogg',
        ext: '.ogg',
        gUM: { audio: true }
    }
    navigator.mediaDevices.getUserMedia(media.gUM).then(_stream => {
        stream = _stream;
        recorder = new MediaRecorder(stream);
        recorder.ondataavailable = e => {
            chunks.push(e.data);
            if (recorder.state == 'inactive') makeLink();  
        };
        log('got media successfully');
    }).catch(log);


    start.onclick = e => {
        start.disabled = true;
        stop.removeAttribute('disabled');
        chunks = [];
        recorder.start();
    }


    stop.onclick = e => {
        stop.disabled = true;
        recorder.stop();
        start.removeAttribute('disabled');
    }
    function makeLink() {
        let blob = new Blob(chunks, { type: media.type })
            , url = URL.createObjectURL(blob)
            , div = document.createElement('div')
            , mt = document.createElement(media.tag)
            , hf = document.createElement('a')
            ;
        mt.controls = true;
        mt.src = url;
        hf.href = url;
        hf.download = `${counter++}${media.ext}`;
        hf.innerHTML = `donwload ${hf.download}`;
        div.appendChild(mt);
        ul.appendChild(div);
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Much appreciated


Solution

  • So just in case anyone else stumples on it, as expected it was quite simple (not the cleanest code but here you go):

    create a new Blob value:

            recorder.ondataavailable = e => {
            chunks.push(e.data);
            superBuffer = new Blob(chunks, { type: 'audio/ogg' });
            if (recorder.state == 'inactive') makeLink(); //console.log(e.data)
    

    Then use Ajax to send this to server:

                var reader = new window.FileReader();
                reader.readAsDataURL(superBuffer);
    
                reader.onloadend = function () {
                    base64 = reader.result;
                    base64 = base64.split(',')[1];
    
                    $.ajax({
                        url: 'MyController/Action1',
                        type: 'POST',
                        data: {
                            audioname: "hello",//obviously change to something dynamic
                            chunks: base64
                        },
                        success: function (response) { console.log(response); },
                        error: function (response) { console.log(response); }
                    });
    

    Then in the code-behind:

            [HttpPost]
        public IActionResult Action1(string audioname, string chunks)
        {
    
            string fileNameWitPath = Path.Combine(_hostingEnvironment.WebRootPath, "audio", "test.ogg");
    
            using (FileStream fs = new FileStream(fileNameWitPath, FileMode.Create))
            {
                using (BinaryWriter bw = new BinaryWriter(fs))
                {
                    byte[] data = Convert.FromBase64String(chunks);
                    bw.Write(data);
                    bw.Close();
                }
                fs.Close();
            }
         return Content(chunks) ;//this is for testing - sends back full chunk on success, would probably just want some confirm all is good message
        }
    

    Note this is work in progress obviously with things to fill, but in general works