环境:
- asp.net 4.5
- Visual Studio 2015
- 本地已经部署deepseek-r1:1.5b
涉及技术
- ASP.NET MVC框架用于构建Web应用程序。
- 使用HttpWebRequest和HttpWebResponse进行HTTP请求和响应处理。
- JSON序列化和反序列化用于构造和解析数据。
- SSE(服务器发送事件)用于向客户端推送实时更新。
完成显示效果如下 (患者信息是AI生成的,非真实信息)
1、在前端界面(cshtml)使用SSE技术向后端传送患者报告的路径。
<!-- 根据系统环境获取PDF文件路径 -->
@{string pdffilepath = datas.ReportFile;}
<div class="deepseek-title"> <!-- DeepSeek输入框 -->
<h4 class="title3">DeepSeek分析</h4>
</div>
<div class="deepseek-textarea">
<textarea class="text-box" id="outputTextarea" readonly></textarea> <!-- 第二行文本框 -->
<button class="text-button" id="reparseButton">重新解析报告</button> <!-- 新增按钮 -->
</div>
<script>
var textarea = document.getElementById('outputTextarea');
var reparseButton = document.getElementById('reparseButton');
var source;
// 初始化SSE连接的函数
function startEventSource() {
// 如果已有连接,先关闭
if (source) {
source.close();
}
// 创建新的SSE连接
source = new EventSource('@Url.Action("StreamAI", "DeepSeek", new { pdfPath = pdffilepath })');
// 接收数据并更新textarea
source.onmessage = function(event) {
if (event.data.includes("换1行")) {
textarea.value += event.data.replace("换1行", "\n"); // 替换为一个换行符
} else if (event.data.includes("换2行")) {
textarea.value += event.data.replace("换2行", "\n\n"); // 替换为两个换行符
} else {
textarea.value += event.data; // 无特殊标记,按原数据添加
}
// 自动滚动到最新内容
//textarea.scrollTop = textarea.scrollHeight; // 恢复滚动功能
};
// 错误处理
source.onerror = function() {
console.log('SSE 连接错误');
source.close();
};
}
// 页面加载时初次启动SSE
startEventSource();
// 按钮点击事件:清空文本框并重新解析
reparseButton.addEventListener('click', function() {
textarea.value = ''; // 清空文本框
startEventSource(); // 重新建立SSE连接
});
</script>
<style>
.title3{
margin: 30px 20px 0px 20px;
margin-top:10px;
}
.text-box {
margin: 10px 20px;
width: 540px;
height: 260px;
font-size: 15px;
outline:0px;
border: 2px solid rgba(228,228,228,1);
border-radius: 4px;
line-height: 25px; /* 设置行距像素 */
}
.text-button{
margin: 20px 20px 0px 20px;
width: 140px;
height: 30px;
background-color: #FFC107;
font-size: 18px;
color: white;
font-family:SourceHanSansCN-Regular;
border: 0px;
border-radius: 4px;
outline: 0;
cursor: pointer; /* 到达按钮旁边时显示手 */
}
</style>
2、后端接收到传递过来的患者报告路径,进行解析,然后发送给DeepSeek分析,DeepSeek分析的内容实时使用Response.Write()推送至前端。
DeepSeekController.cs
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Web.Mvc;
using Newtonsoft.Json;
namespace *********
{
public class DeepSeekController : Controller
{
public ActionResult StreamAI(string pdfPath)
{
// 提取 PDF 文本
string pdfText = DeepSeekHelper.ExtractTextFromPdf(pdfPath);
if (string.IsNullOrEmpty(pdfText))
{
return Content("PDF文件不存在或无法读取", "text/event-stream");
}
// 设置 SSE 响应头
Response.ContentType = "text/event-stream";
Response.Headers.Add("Cache-Control", "no-cache");
Response.Headers.Add("Connection", "keep-alive");
// 构造 AI 请求数据
var requestData = new
{
model = "deepseek-r1:1.5b",
prompt = $"请以专业医生的身份,对以下患者报告进行详细分析,并提供医学建议:{pdfText}请按照以下结构输出:影像/检查结果分析:详细解读报告中的影像表现或检查结果,结合医学知识说明异常情况及其可能的临床意义。诊断建议:根据分析结果,提出初步诊断方向(如疾病名称或疑似病因)。请确保内容准确、专业,并符合临床实践规范",
stream = true
};
string jsonContent = JsonConvert.SerializeObject(requestData);
byte[] byteArray = Encoding.UTF8.GetBytes(jsonContent);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:11434/api/generate");
request.Method = "POST";
request.ContentType = "application/json";
request.ContentLength = byteArray.Length;
try
{
using (Stream dataStream = request.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
}
//string linestrs = "";
try
{
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream responseStream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(responseStream))
{
string line;
while ((line = reader.ReadLine()) != null)
{
if (!string.IsNullOrEmpty(line))
{
dynamic result = JsonConvert.DeserializeObject(line);
if (result != null && result.response != null)
{
string responseText = result.response.ToString();
string processedText = DeepSeekHelper.RegexLine(responseText);
if (processedText.Contains("\n\n"))
{
processedText = processedText.Replace("\n\n", "换2行");
}
else if (processedText.Contains("\n"))
{
processedText = processedText.Replace("\n\n", "换1行");
}
//linestrs += processedText;
//推送数据到客户端
Response.Write($"data: {processedText}\n\n");
Response.Flush();
}
}
}
}
}
catch (WebException ex)
{
Response.Write($"data: 错误:{ex.Message}\n\n");
Response.Flush();
}
}
catch
{
try
{
// 返回自定义错误信息
Response.Write($"data: 错误:请配置本地DeepSeek,或者启动相关服务。\n\n");
Response.Flush();
}
catch
{
}
}
return new EmptyResult();
}
}
}
DeepSeekHelper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;
using System.Text;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using Newtonsoft.Json;
namespace *******
{
public static class DeepSeekHelper
{
/// <summary>
/// 读取PDF文件获取PDF中的文字
/// </summary>
/// <param name="pdfPath"></param>
/// <returns></returns>
public static string ExtractTextFromPdf(string pdfPath)
{
string pdfPath2 = pdfPath.Replace("_1.png", ".pdf");
if (!File.Exists(pdfPath2))
{
Console.WriteLine("PDF文件不存在!");
return "";
}
PdfReader reader = new PdfReader(pdfPath2); // 直接创建PdfReader对象
string strs = "";
for (int page = 1; page <= reader.NumberOfPages; page++)
{
ITextExtractionStrategy strategy = new SimpleTextExtractionStrategy();
string currentText = PdfTextExtractor.GetTextFromPage(reader, page, strategy);
strs += currentText;
}
reader.Close(); // 手动关闭PdfReader对象
return strs;
}
/// <summary>
/// 处理DeepSeek返回的字符串
/// </summary>
/// <param name="responseText"></param>
/// <returns></returns>
public static string RegexLine(string responseText)
{
// 使用正则表达式去掉 <think> 和 </think> 标签
responseText = Regex.Replace(responseText, @"<\/?think>", "\n");
// 去掉开头的换行符
responseText = responseText.TrimStart('\r', '\n');
return responseText;
}
}
}
总结
- 前端技术:HTML、JavaScript(使用EventSource实现SSE)。
- 后端技术:C# ASP.NET MVC、HTTP请求/响应处理、JSON处理。
- 通信技术:SSE用于服务器向客户端推送实时数据。
- 功能:实现了一个可以解析PDF文件内容,通过AI服务进行医学报告分析,并将结果实时推送给前端显示的应用程序。