将大型 JS blob 传递给 Blazor byte[]

Pass large JS blob to Blazor byte[]

我需要使用 Media API in JS, example 为 Blazor 录制一些音频,甚至可能是视频。然后,我想将记录的 Blob 内容从 JS 传递到 Blazor。只要是音频或视频内容,它就可以相当大。

到目前为止我尝试了什么

  1. 将数据编码为 ANSI 字符串或传递一个整数数组。这导致 InvalidDataException、SignalR 断开连接、超时,大约一分钟后 SignalR 恢复正常并且 C# 收到 null

  2. 将数据编码为 base 64 或传递 UInt8Array。结果是一样的。

  3. 直接从 JS 传递 BlobArrayBufferFormData。这导致 C# 中的空对象 ValueKind: {}

JS调用来源

recorder.stop();
recorder.exportWAV(async blob => {
  const content = await (new Response(blob).arrayBuffer());
  const contentNums = new Uint8Array(content);
  const contentCodes = new TextDecoder('windows-1252').decode(contentNums);
  const data = new FormData();
  data.append('file', blob, 'Demo');
  //success(window.URL.createObjectURL(blob));
  console.log(blob)
  console.log(content)
  console.log(contentNums)
  console.log(contentCodes)
  success(Array.from(contentNums));
})

C#互操作调用来源

private IJSRuntime _scriptRuntime = null;

public async Task<dynamic> GetMedia<dynamic>()
{
  return await _scriptRuntime.InvokeAsync<dynamic>('AudioFunctions.GetMediaFile');
}

有没有办法将大字节数组或至少字符串从 JS 传递到 Blazor.NET?

看来,使用 ANSI 编码的破解工作正常,我只需要增加 SignalR 消息的大小。

启动

public void ConfigureServices(IServiceCollection services)
{
  // ... some other services

  services.AddSignalR(o =>
  {
    o.EnableDetailedErrors = true;
    o.MaximumReceiveMessageSize = long.MaxValue;
  });
}

JS 互操作

window.InteropFunctions = window.InteropFunctions || {

  GetMediaFile: async (classInstance, cssClass) => {

    return await new Promise((success, error) => {

      const chunks = [];
      const recorder = new MediaRecorder(stream, { mimeType: 'audio/webm' });

      // Event handlers for play and stop to detect when data is available

      recorder.addEventListener('stop', {

        // Encode data as ANSI string - Windows 1252 

        const response = new Blob(chunks, { type: 'audio/webm' });
        const content = await (new Response(response).arrayBuffer());
        const contentNums = new Uint8Array(content);
        const contentCodes = new TextDecoder('windows-1252').decode(contentNums);
        const audioControl = document.querySelector('.' + cssClass + ' audio');

        // Play audio in HTML 5 control

        if (audioControl) {
          audioControl.src = URL.createObjectURL(response);
        }

        // Resolve the promise and send data to Blazor.NET

        success(contentCodes);
      });

      // Grab recorded data
      
      recorder.addEventListener('dataavailable', {
        if (e.data.size > 0) {
          chunks.push(e.data);
        }
      });
      
      // Record for 5 seconds
      
      recorder.start();      
      setTimeout(() => recorder.stop(), 5000);
    });
  }
}

C# 互操作

private IJSRuntime _scriptRuntime = null;

public async Task<string> GetMediaFile<string>(params object[] inputs)
{
  return await _scriptRuntime.InvokeAsync<string>('InteropFunctions.GetMediaFile', inputs);
}

从 .NET 发起互操作调用

var instance = DotNetObjectReference.Create(this);
var source = await _audioCommandInstance.GetMediaFile(instance, 'audio-container');

if (string.IsNullOrEmpty(source) == false)
{
  var audioBytes = Encoder.GetBytes(source);
  
  await File.WriteAllBytesAsync("audio.wav", audioBytes);
}