// ============================================================
// UmiOcrHost:Umi-OCR 本体(网盘 zip)下载/安装/检测 管理器
// · 本体 zip 直链(用户网盘,~179MB,zip 根直接是 Umi-OCR.exe 无顶层目录)
// · 安装 = 下载 zip 到临时文件 → 解压到安装目录 → 验证主程序 → 删 zip
// · 启动/停止在 UmiOcrLauncher(进程管理独立成文件)
// · 安装目录:主 exe 旁 Umi-OCR\;装过独立版 pdfbt 的机器回退 pdfbt\Umi-OCR\ 旧本体
// ============================================================
using System;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Threading.Tasks;
namespace 成套报价软件
{
internal static class UmiOcrHost
{
/// 本体 zip 直链(用户网盘直连,GET 才有内容,HEAD 会 404,不支持断点续传)。
public const string DownloadUrl = "http://s333.odn.cc/s/845f64a239b665ec";
private const string InstallDirName = "Umi-OCR";
private const string ExeName = "Umi-OCR.exe"; // 官方 zip 根的主程序固定文件名
private const int HttpPort = 1224;
/// 本体安装目录(主 exe 旁 Umi-OCR\;独立版 pdfbt 旧布局回退)。
public static string InstallDir
{
get
{
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
string dir = Path.Combine(baseDir, InstallDirName);
if (Directory.Exists(dir)) return dir;
// 独立版 pdfbt 已下载过本体的机器:沿用旧安装
string legacy = Path.Combine(baseDir, "pdfbt", InstallDirName);
if (Directory.Exists(legacy)) return legacy;
return dir;
}
}
/// 安装目录里主程序的固定路径(File.Exists 忽略大小写,覆盖官方 zip 命名)。
public static string ExePath
{
get { return Path.Combine(InstallDir, ExeName); }
}
/// 本体是否已安装(主程序文件存在)。
public static bool IsInstalled
{
get { return File.Exists(ExePath); }
}
/// HTTP 服务(1224 端口)是否在监听。
public static bool IsHttpUp()
{
try
{
using (var c = new System.Net.Sockets.TcpClient())
{
var ar = c.BeginConnect("127.0.0.1", HttpPort, null, null);
if (ar.AsyncWaitHandle.WaitOne(800))
{
c.EndConnect(ar);
return true;
}
c.Close();
}
}
catch { }
return false;
}
/// 三态:0=未安装,1=已安装未运行,2=运行中(HTTP 在线)。
public static int Detect()
{
if (!IsInstalled) return 0;
return IsHttpUp() ? 2 : 1;
}
/// 状态文字(设置窗口显示用)。
public static string DetectText()
{
int st = Detect();
if (st == 0) return "未检测到 OCR 本体(点\"下载 OCR 本体\"自动下载安装)";
if (st == 1) return "已检测到 OCR 本体,服务未运行(识别时会自动启动)";
return "OCR 本体运行中(HTTP " + HttpPort + " 端口在线)";
}
/// 下载 zip → 解压到安装目录 → 验证主程序 → 删 zip。
/// progress: 0~100 下载百分比;解压与收尾阶段回调 100。失败抛异常(内部已清理残留)。
/// ★ 必须用 DownloadFileAsync:同步版 DownloadFile 不触发 DownloadProgressChanged
/// (进度事件只在异步路径发,同步下载会一直停在 0%)。
public static Task DownloadAndInstallAsync(Action progress)
{
return Task.Factory.StartNew(delegate
{
string tmpZip = Path.Combine(Path.GetTempPath(), "Umi-OCR_" + Guid.NewGuid().ToString("N") + ".zip");
try
{
if (progress != null) progress(0);
// 异步下载包成同步等待(TaskCompletionSource),进度事件才会触发
var dlTcs = new TaskCompletionSource();
using (var wc = new WebClient())
{
wc.Proxy = null;
wc.DownloadProgressChanged += (s, e) =>
{
if (progress != null) progress(e.ProgressPercentage);
};
wc.DownloadFileCompleted += (s, e) =>
{
if (e.Error != null) dlTcs.TrySetException(e.Error);
else if (e.Cancelled) dlTcs.TrySetException(new Exception("下载已取消"));
else dlTcs.TrySetResult(true);
};
wc.DownloadFileAsync(new Uri(DownloadUrl), tmpZip);
}
dlTcs.Task.GetAwaiter().GetResult(); // 等下载结束(异常原样抛出)
if (progress != null) progress(100);
// 校验是 zip(PK 头)而不是错误页/JSON
using (var fs = File.OpenRead(tmpZip))
{
int b0 = fs.ReadByte(), b1 = fs.ReadByte();
if (b0 != 'P' || b1 != 'K')
throw new Exception("下载的内容不是有效的压缩包(可能是网盘临时故障),请稍后重试");
}
// 解压(覆盖);zip 根直接是 Umi-OCR.exe,目标目录即安装目录
string dst = InstallDir;
if (!Directory.Exists(dst)) Directory.CreateDirectory(dst);
using (var archive = ZipFile.OpenRead(tmpZip))
{
foreach (var entry in archive.Entries)
{
string target = Path.GetFullPath(Path.Combine(dst, entry.FullName));
if (!target.StartsWith(Path.GetFullPath(dst), StringComparison.OrdinalIgnoreCase))
continue; // 防 zip 路径穿越
if (string.IsNullOrEmpty(entry.Name))
{
if (!Directory.Exists(target)) Directory.CreateDirectory(target);
continue;
}
string tdir = Path.GetDirectoryName(target);
if (!string.IsNullOrEmpty(tdir) && !Directory.Exists(tdir)) Directory.CreateDirectory(tdir);
if (File.Exists(target))
{
try { File.Delete(target); } catch { } // 被占用则跳过(旧文件同名多半一致)
}
entry.ExtractToFile(target, false);
}
}
if (!IsInstalled)
throw new Exception("解压完成但未找到 Umi-OCR 主程序,压缩包内容可能不完整");
try { File.Delete(tmpZip); } catch { } // 验证通过后删 zip
}
catch
{
try { if (File.Exists(tmpZip)) File.Delete(tmpZip); } catch { }
throw;
}
});
}
}
}