• ASP.NET Core Web API 流式返回,逐字显示


    Websocket、SSE(Server-Sent Events)和长轮询(Long Polling)都是用于网页和服务端通信的技术。

    Websocket是一种全双工通信协议,能够实现客户端和服务端之间的实时通信。它基于TCP协议,并且允许服务器主动向客户端推送数据,同时也允许客户端向服务器发送数据。

    SSE是一种单向通信协议,允许服务器向客户端推送数据,但不支持客户端向服务器发送数据。SSE建立在HTTP协议上,通过在HTTP响应中使用特殊的Content-Type和事件流(event stream)格式来实现。

    长轮询是一种技术,客户端向服务器发送一个请求,并且服务器保持连接打开直到有数据可以返回给客户端。如果在指定的时间内没有数据可用,则服务器会关闭连接,客户端需要重新建立连接并再次发起请求。

    New Bing聊天页面是通过WebSocket进行通信。
    在这里插入图片描述
    Open AI的ChatGPT接口则是通过SSE协议由服务端推送数据
    在这里插入图片描述
    事实上,以上几种方式包括长轮询,都可以实现逐字显示的效果。那还有没有其他的办法可以实现这种效果了呢?

    流式响应

    当客户端返回流的时候,客户端可以实时捕获到返回的信息,并不需要等全部Response结束了再处理。

    下面就用ASP.NET Core Web API作为服务端实现流式响应。

    返回文本内容

    服务端

    [HttpPost("text")]
    public async Task Post()
    {
        string filePath = "文档.txt";
        Response.ContentType = "application/octet-stream";
        var reader = new StreamReader(filePath);
        var buffer = new Memory<char>(new char[5]);
        int writeLength = 0;
        //每次读取5个字符写入到流中
        while ((writeLength = await reader.ReadBlockAsync(buffer)) > 0)
        {
            if (writeLength < buffer.Length)
            {
            	buffer = buffer[..writeLength];
            }
            await Response.WriteAsync(buffer.ToString());
            await Task.Delay(100);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    客户端

    • C# HttpClient
    public async void GetText()
    {
        var url = "http://localhost:5000/config/text";
        var client = new HttpClient();
        using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, url);
        var response = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead);
        await using var stream = await response.Content.ReadAsStreamAsync();
        var streamReader = new StreamReader(stream);
        var buffer = new Memory<char>(new char[5]);
        int writeLength = 0;
        while ((writeLength = await streamReader.ReadBlockAsync(buffer)) > 0)
        {
            if (writeLength < buffer.Length)
            {
                buffer = buffer[..writeLength];
            }
            Console.Write(buffer.ToString());
        }
        Console.WriteLine();
        Console.WriteLine("END");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    HttpCompletionOption枚举有两个值,默认情况下使用的是ResponseContentRead

    ResponseContentRead:等到整个响应完成才完成操作

    ResponseHeadersRead:一旦获取到响应头即完成操作,不用等到整个内容响应
    在这里插入图片描述

    js XMLHttpRequest

    <script>
        var div = document.getElementById("content")
        var url = "http://localhost:5000/config/text"
        var client = new XMLHttpRequest()
        client.open("POST", url)
        client.onprogress = function (progressEvent) {
            div.innerText = progressEvent.target.responseText
        }
        client.onloadend = function (progressEvent) {
            div.append("END")
        }
        client.send()
    
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    用axios请求就是监听onDownloadProgress了。

    浏览器是通过Response
    Header中的Content-Type来解析服务端响应体的。如果后端接口没有设置Response.ContentType =
    "application/octet-stream"onprogress只会在响应全部完成后触发。
    在这里插入图片描述

    返回图片

    服务端

    [HttpGet("img")]
    public async Task Stream()
    {
        string filePath = "pixelcity.png";
        new FileExtensionContentTypeProvider().TryGetContentType(filePath, out string contentType);
        Response.ContentType = contentType ?? "application/octet-stream";
        var fileStream = System.IO.File.OpenRead(filePath);
        var bytes = new byte[1024];
        int writeLength = 0;
        while ((writeLength = fileStream.Read(bytes, 0, bytes.Length)) > 0)
        {
            await Response.Body.WriteAsync(bytes, 0, writeLength);
            await Task.Delay(100);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

  • 相关阅读:
    机器学习垃圾邮件分类
    目标检测之多尺度融合
    Vue 3-150行代码实现新国标红绿灯效果案例
    java桌面程序
    ChatGPT的一小步,NLP范式转变的一大步
    (16)Blender源码分析之闪屏窗口的菜单从python加载过程
    vscode launch.json
    【JavaSE终幕式】反射枚举与Lambda表达式
    Java程序启动后首次访问很慢--JVM预热
    ESP 特权隔离机制—案例研究
  • 原文地址:https://blog.csdn.net/weixin_44837958/article/details/136700593