Search code examples
c#signalr.net-coreasp.net-core-signalr

how to send a binary file to SignalR client from server in dotnet core


We had a solution for sending a file to SignalR client using .Net We have now moved to .Net Core In previous .net solution, we used to Hub context via GlobalHost.ConnectionManager

var myHub = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
myHub.Clients.Client(connectionId).doStuffWithFile(fileByteArray, fileName);

where in the client side, function doStuffWithFile would be triggered with the two arguments.

In new .Net Core solution I created a Hub class by deriving from Hub. I added a method of Send to send a file to specific client and not broadcasting it to every one

public class MyHub : Hub
{   
    private static string _connectionId;    

    public override Task OnConnectedAsync()
    {
        _connectionId = Context.ConnectionId;
        return Task.CompletedTask;
    }

    public override Task OnDisconnectedAsync(Exception exception)
    {
        _connectionId = Context.ConnectionId;

        //// remove Connection Id
        return base.OnDisconnectedAsync(exception);
    }

    public async Task Send(byte[] fileByteArray, string fileName)
    {
        await Clients.Client(_connectionId).InvokeAsync("doStuff", fileByteArray, fileName);
    }
}

However, I do not have any mechanism in .Net core such as GlobalHost or ConnectionManager to get HubContext to send the file.

On the client side:

static void Main(string[] args)
        {
            var connection = new HubConnectionBuilder()
                .WithUrl("http://localhost:25786/file")
                .WithConsoleLogger()
                .Build();
            connection.On<byte[], string>("doStuff", DoStuff);

            connection.StartAsync().ContinueWith(
                task =>
                {
                    if (task.IsFaulted)
                    {
                        Console.WriteLine("Connection faulty");
                    }
                });

            Console.ReadLine();
}
private static void DoStuff(byte[] data, string name)
{
   File.WriteAllBytes(@"c:\Projects\" + name, data);
}

I tried to create a new instance of MyHub to invoke the Send method, but simply it does not work. Could you please advise me how to do this?


Solution

  • This is not a direct answer to your question but hopefully it will help you find a solution.

    Storing connection Id in a static class variable is not correct. It will change each time a new client connects and you won't have any control over which client you are sending to. From the code you provided it is not clear how you know which client to send the file to. Note that you also set the _connectionId when the client is disconnected so it is likely that you will try to send data to the connection you know has been closed. I actually think you will want to pass the target connection id or the user to the hub Send method. I think you may not have enough context in the hub method itself to resolve the connection id but since connection id is a SignalR concept it might be hard to access it outside SignalR components. This is why it might be easier to use the user instead of connection Id (i.e. Clients.User(...) instead of Clients.Client(...)).

    GlobalHost no longer exists in the new SignalR. Instead you can inject IHubContext<THub> to the class you want to invoke the method from and use InvokeAsync. Here is an example of invoking a hub method from an Mvc Controller.