Servir archivos incrustados en la biblioteca .net a HTML en Blazor

.net-standard blazor c# embedded-resource javascript

Pregunta

Estoy creando un Blazor v0.5.1 proyecto que está utilizando una biblioteca .NET estándar a proporcionado toda la lógica de negocio. Esta biblioteca tiene varios archivos WAV almacenados como recursos incrustados.

El consumo de uno de los recursos en una tecnología .NET típica (WinForms, WPF, etc.) que hace referencia a esta biblioteca se puede hacer con esto:

var assemName = "MyLibName";
var assembly = AppDomain
    .CurrentDomain
    .GetAssemblies()
    .First(a => a.GetName().Name == assemName);

//memory stream
var stream = assembly.GetManifestResourceStream($"{assemName}.mysound.wav");

//Can play the file at some point later
var player = new SoundPlayer(stream)
player.Play();

Me gustaría hacer el equivalente en Blazor. En este momento, tengo sonidos trabajando en la aplicación al copiar / pegar los archivos wav en la carpeta wwwroot\sounds del proyecto para que se sirva como contenido HTML estático. Luego, en JavaScript, puedo jugar uno así:

const audio = new Audio"\sounds\mysound.wave"]);
audio.currentTime = 0;
audio.play();

Pero lo que realmente me gustaría hacer es evitar copiar y pegar y de alguna manera servir los archivos dinámicamente como puntos finales para que sea transparente para JS.

Respuesta popular

Bueno, pude conseguirlo. No estoy seguro si es de la mejor manera pero parece funcionar bien.

Tuve que añadir la extensión de almacenamiento blazor:

https://github.com/BlazorExtensions/Storage

que actúa como un proxy para el SessionStorage de JavaScript y LocalStorage de .NET. Una vez cargado, agregué cada archivo wav incrustado desde .NET de esta manera:

foreach (var kvp in SoundStreamDictionary)
{
    await sessionStorage.SetItem(
        kvp.Key.ToString().ToLower()  //Key is the name of the sound
        , kvp.Value.ToBase64()  //Value is a Stream object
    );
}

El ToBase64 es bastante estándar para la manipulación de secuencias .NET:

    public static string ToBase64(this Stream stream)
    {
        byte[] bytes;
        using (var memoryStream = new MemoryStream())
        {
            stream.Position = 0;
            stream.CopyTo(memoryStream);
            bytes = memoryStream.ToArray();
        }

        return Convert.ToBase64String(bytes);
    }

Ahora todos los datos se almacenan en JavaScript SessionStorage como cadenas. El truco ahora es decodificar eso para audio. Con la ayuda de este método de ayuda JS (gracias a esta publicación de StackOverflow ):

function b64toBlob(b64Data, contentType, sliceSize) {
    contentType = contentType || '';
    sliceSize = sliceSize || 512;

    const byteCharacters = atob(b64Data);
    const byteArrays = [];

    for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        const slice = byteCharacters.slice(offset, offset + sliceSize);

        const byteNumbers = new Array(slice.length);
        for (let i = 0; i < slice.length; i++) {
            byteNumbers[i] = slice.charCodeAt(i);
        }

        const byteArray = new Uint8Array(byteNumbers);

        byteArrays.push(byteArray);
    }

    return new Blob(byteArrays, { type: contentType });
}

Ponga eso y esto en el archivo JsInterop.js de BlazorComponent:

const soundAudios = [];

window.JsSound = {

    loadSounds: function (sounds) {
        sounds.forEach(sound => {
            //Blazor.Storage extension adds extra double quotes so trim
            const str64 = sessionStorage.getItem(sound.path).slice(1, -1);

            const blob = b64toBlob(str64, "audio/wav");
            const blobUrl = URL.createObjectURL(blob);

            const audio = new Audio(blobUrl);
            soundAudios[sound.id] = audio;
        });

        return true;
    },

    play: function (id) {
        const audio = soundAudios[id];
        audio.currentTime = 0;
        audio.play();

        return true;
    },
};

Y FINALMENTE los sonidos pueden ser invocados por nombre desde .Net:

private IDictionary<string, int> soundDict = new Dictionary<string, int>();

public Task<string> LoadSounds(IEnumerable<string> fileNames)
{
    var sounds = fileNames
        .Select(name =>
        {
            var snd = new
            {
                id = soundDict.Count,
                path = name
            };
            soundDict.Add(name, snd.id);

            return snd;
        })
        .ToList();

    return JSRuntime.Current.InvokeAsync<string>(
        "JsSound.loadSounds"
        , sounds
    );
}

public Task<string> Play(string name)
{
    return JSRuntime.Current.InvokeAsync<string>(
        "JsSound.play"
        , soundDict[name]
    );
}


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow