FileProcessingService.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. 
  2. using Aspose.Words;
  3. using Aspose.Words.Tables;
  4. using Aspose.Words.Fields;
  5. using System.Text;
  6. namespace OASystem.API.OAMethodLib.FileProcessing
  7. {
  8. /// <summary>
  9. /// 文件处理服务,使用Aspose.Words处理Word文档
  10. /// </summary>
  11. public class FileProcessingService
  12. //: IFileProcessingService
  13. {
  14. private readonly ILogger<FileProcessingService> _logger;
  15. private readonly IConfiguration _configuration;
  16. public FileProcessingService(
  17. ILogger<FileProcessingService> logger,
  18. IConfiguration configuration)
  19. {
  20. _logger = logger;
  21. _configuration = configuration;
  22. InitializeAsposeLicense();
  23. }
  24. /// <summary>
  25. /// 初始化Aspose.Words许可证
  26. /// </summary>
  27. private void InitializeAsposeLicense()
  28. {
  29. try
  30. {
  31. var licensePath = _configuration["Aspose:LicensePath"];
  32. if (!string.IsNullOrEmpty(licensePath) && System.IO.File.Exists(licensePath))
  33. {
  34. var license = new License();
  35. license.SetLicense(licensePath);
  36. _logger.LogInformation("Aspose.Words 15.12.0 许可证已成功加载");
  37. }
  38. else
  39. {
  40. _logger.LogWarning("Aspose.Words许可证文件未找到,将使用评估模式");
  41. }
  42. }
  43. catch (Exception ex)
  44. {
  45. _logger.LogError(ex, "Aspose.Words许可证初始化失败");
  46. }
  47. }
  48. ///// <summary>
  49. ///// 处理Word文档并提取结构化信息
  50. ///// </summary>
  51. //public async Task<ProcessingResult> ProcessWordDocumentAsync(IFormFile file)
  52. //{
  53. // var stopwatch = System.Diagnostics.Stopwatch.StartNew();
  54. // try
  55. // {
  56. // _logger.LogInformation("开始处理Word文档: {FileName} ({Size} bytes)",
  57. // file.FileName, file.Length);
  58. // if (!IsSupportedWordFormat(file.FileName))
  59. // {
  60. // return new ProcessingResult
  61. // {
  62. // Success = false,
  63. // ErrorMessage = $"不支持的文件格式: {Path.GetExtension(file.FileName)}",
  64. // FileSize = file.Length,
  65. // FileType = file.ContentType
  66. // };
  67. // }
  68. // using var stream = new MemoryStream();
  69. // await file.CopyToAsync(stream);
  70. // stream.Position = 0;
  71. // var documentInfo = await ExtractDocumentInfoAsync(stream, file.FileName);
  72. // stopwatch.Stop();
  73. // _logger.LogInformation("Word文档处理完成: {FileName}, 耗时: {ElapsedMs}ms",
  74. // file.FileName, stopwatch.ElapsedMilliseconds);
  75. // return new ProcessingResult
  76. // {
  77. // Success = true,
  78. // Data = documentInfo,
  79. // FileSize = file.Length,
  80. // FileType = file.ContentType
  81. // };
  82. // }
  83. // catch (Exception ex)
  84. // {
  85. // stopwatch.Stop();
  86. // _logger.LogError(ex, "Word文档处理失败: {FileName}", file.FileName);
  87. // return new ProcessingResult
  88. // {
  89. // Success = false,
  90. // ErrorMessage = $"处理失败: {ex.Message}",
  91. // FileSize = file.Length,
  92. // FileType = file.ContentType
  93. // };
  94. // }
  95. //}
  96. ///// <summary>
  97. ///// 批量处理Word文档
  98. ///// </summary>
  99. //public async Task<List<ProcessingResult>> ProcessWordDocumentsAsync(List<IFormFile> files)
  100. //{
  101. // var results = new List<ProcessingResult>();
  102. // var tasks = files.Select(ProcessWordDocumentAsync).ToList();
  103. // var batchResults = await Task.WhenAll(tasks);
  104. // results.AddRange(batchResults);
  105. // return results;
  106. //}
  107. ///// <summary>
  108. ///// 从流中提取文档信息
  109. ///// </summary>
  110. //public async Task<WordDocumentInfo> ExtractDocumentInfoAsync(Stream stream, string fileName)
  111. //{
  112. // var stopwatch = System.Diagnostics.Stopwatch.StartNew();
  113. // try
  114. // {
  115. // // 加载Word文档
  116. // var doc = new Document(stream);
  117. // var documentInfo = new WordDocumentInfo
  118. // {
  119. // Title = ExtractDocumentTitle(doc),
  120. // Content = ExtractTextContent(doc),
  121. // Metadata = ExtractMetadata(doc),
  122. // Tables = ExtractTables(doc),
  123. // FormFields = ExtractFormFields(doc),
  124. // Sections = ExtractSections(doc),
  125. // ImagesCount = CountImages(doc)
  126. // };
  127. // stopwatch.Stop();
  128. // documentInfo.ProcessingTimeMs = stopwatch.ElapsedMilliseconds;
  129. // return documentInfo;
  130. // }
  131. // catch (Exception ex)
  132. // {
  133. // _logger.LogError(ex, "文档信息提取失败: {FileName}", fileName);
  134. // throw;
  135. // }
  136. //}
  137. ///// <summary>
  138. ///// 提取文档标题
  139. ///// </summary>
  140. //private string ExtractDocumentTitle(Document doc)
  141. //{
  142. // try
  143. // {
  144. // // 首先尝试从文档属性获取标题
  145. // var title = doc.BuiltInDocumentProperties.Title;
  146. // if (!string.IsNullOrEmpty(title))
  147. // return title;
  148. // // 如果没有标题,尝试从第一个段落提取
  149. // foreach (Aspose.Words.Paragraph paragraph in doc.GetChildNodes(NodeType.Paragraph, true))
  150. // {
  151. // var text = paragraph.GetText().Trim();
  152. // if (!string.IsNullOrEmpty(text) && text.Length < 100) // 假设标题不会太长
  153. // return text;
  154. // }
  155. // return "未命名文档";
  156. // }
  157. // catch (Exception ex)
  158. // {
  159. // _logger.LogWarning(ex, "提取文档标题失败");
  160. // return "未命名文档";
  161. // }
  162. //}
  163. ///// <summary>
  164. ///// 提取文本内容
  165. ///// </summary>
  166. //private string ExtractTextContent(Document doc)
  167. //{
  168. // try
  169. // {
  170. // // 使用Aspose.Words的GetText方法提取纯文本
  171. // return doc.GetText();
  172. // }
  173. // catch (Exception ex)
  174. // {
  175. // _logger.LogWarning(ex, "提取文本内容失败");
  176. // return string.Empty;
  177. // }
  178. //}
  179. ///// <summary>
  180. ///// 提取文档元数据
  181. ///// </summary>
  182. //private DocumentMetadata ExtractMetadata(Document doc)
  183. //{
  184. // try
  185. // {
  186. // var props = doc.BuiltInDocumentProperties;
  187. // return new DocumentMetadata
  188. // {
  189. // Author = props.Author ?? string.Empty,
  190. // Company = props.Company ?? string.Empty,
  191. // CreatedTime = props.CreatedTime,
  192. // LastSavedTime = props.LastSavedTime,
  193. // PageCount = doc.PageCount,
  194. // WordCount = props.Words,
  195. // CharacterCount = props.Characters,
  196. // Subject = props.Subject ?? string.Empty,
  197. // Keywords = props.Keywords ?? string.Empty
  198. // };
  199. // }
  200. // catch (Exception ex)
  201. // {
  202. // _logger.LogWarning(ex, "提取元数据失败");
  203. // return new DocumentMetadata();
  204. // }
  205. //}
  206. ///// <summary>
  207. ///// 提取表格数据
  208. ///// </summary>
  209. //private List<DocumentTable> ExtractTables(Document doc)
  210. //{
  211. // var tables = new List<DocumentTable>();
  212. // try
  213. // {
  214. // int tableIndex = 1;
  215. // foreach (Aspose.Words.Tables.Table table in doc.GetChildNodes(NodeType.Table, true))
  216. // {
  217. // var docTable = new DocumentTable
  218. // {
  219. // TableName = $"表格_{tableIndex}"
  220. // };
  221. // // 提取表头(假设第一行是表头)
  222. // if (table.Rows.Count > 0)
  223. // {
  224. // var firstRow = table.FirstRow;
  225. // foreach (Cell cell in firstRow.Cells)
  226. // {
  227. // docTable.Headers.Add(cell.GetText().Trim());
  228. // }
  229. // }
  230. // // 提取所有行数据
  231. // foreach (Row row in table.Rows)
  232. // {
  233. // var rowData = new List<string>();
  234. // foreach (Cell cell in row.Cells)
  235. // {
  236. // rowData.Add(cell.GetText().Trim());
  237. // }
  238. // docTable.Rows.Add(rowData);
  239. // }
  240. // tables.Add(docTable);
  241. // tableIndex++;
  242. // }
  243. // }
  244. // catch (Exception ex)
  245. // {
  246. // _logger.LogWarning(ex, "提取表格数据失败");
  247. // }
  248. // return tables;
  249. //}
  250. ///// <summary>
  251. ///// 提取表单字段(兼容Aspose.Words 15.12.0)
  252. ///// </summary>
  253. //private List<FormField> ExtractFormFields(Document doc)
  254. //{
  255. // var formFields = new List<FormField>();
  256. // try
  257. // {
  258. // // 获取文档中的所有表单字段
  259. // var formFieldCollection = doc.Range.FormFields;
  260. // for (int i = 0; i < formFieldCollection.Count; i++)
  261. // {
  262. // var formField = formFieldCollection[i];
  263. // var extractedField = ExtractFormFieldInfo(formField);
  264. // if (extractedField != null)
  265. // {
  266. // formFields.Add(extractedField);
  267. // }
  268. // }
  269. // }
  270. // catch (Exception ex)
  271. // {
  272. // _logger.LogWarning(ex, "提取表单字段失败");
  273. // }
  274. // return formFields;
  275. //}
  276. ///// <summary>
  277. ///// 提取单个表单字段的详细信息
  278. ///// </summary>
  279. //private FormField ExtractFormFieldInfo(Aspose.Words.Fields.FormField formField)
  280. //{
  281. // try
  282. // {
  283. // var field = new FormField
  284. // {
  285. // Name = GetFormFieldName(formField),
  286. // Type = GetFormFieldType(formField),
  287. // Value = GetFormFieldValue(formField),
  288. // IsChecked = IsFormFieldChecked(formField),
  289. // Status = GetFormFieldStatus(formField),
  290. // DefaultValue = GetFormFieldDefaultValue(formField),
  291. // MaxLength = GetFormFieldMaxLength(formField),
  292. // Options = GetFormFieldOptions(formField)
  293. // };
  294. // return field;
  295. // }
  296. // catch (Exception ex)
  297. // {
  298. // _logger.LogWarning(ex, "提取表单字段信息失败: {FieldName}", formField.Name);
  299. // return null;
  300. // }
  301. //}
  302. ///// <summary>
  303. ///// 获取表单字段名称
  304. ///// </summary>
  305. //private string GetFormFieldName(Aspose.Words.Fields.FormField formField)
  306. //{
  307. // try
  308. // {
  309. // return !string.IsNullOrEmpty(formField.Name) ? formField.Name : "未命名字段";
  310. // }
  311. // catch
  312. // {
  313. // return "未知字段";
  314. // }
  315. //}
  316. ///// <summary>
  317. ///// 获取表单字段类型
  318. ///// </summary>
  319. //private string GetFormFieldType(Aspose.Words.Fields.FormField formField)
  320. //{
  321. // try
  322. // {
  323. // return formField.Type switch
  324. // {
  325. // FormFieldType.Regular => "常规文本",
  326. // FormFieldType.CheckBox => "复选框",
  327. // FormFieldType.DropDown => "下拉列表",
  328. // _ => "未知类型"
  329. // };
  330. // }
  331. // catch
  332. // {
  333. // return "未知类型";
  334. // }
  335. //}
  336. ///// <summary>
  337. ///// 获取表单字段值
  338. ///// </summary>
  339. //private string GetFormFieldValue(Aspose.Words.Fields.FormField formField)
  340. //{
  341. // try
  342. // {
  343. // // 对于15.12.0版本,使用GetFieldCode()和其他方法获取值
  344. // switch (formField.Type)
  345. // {
  346. // case FormFieldType.CheckBox:
  347. // return IsFormFieldChecked(formField) ? "选中" : "未选中";
  348. // case FormFieldType.DropDown:
  349. // return GetDropDownSelectedValue(formField);
  350. // case FormFieldType.Regular:
  351. // return GetTextFormFieldValue(formField);
  352. // default:
  353. // return string.Empty;
  354. // }
  355. // }
  356. // catch
  357. // {
  358. // return string.Empty;
  359. // }
  360. //}
  361. ///// <summary>
  362. ///// 检查复选框是否被选中
  363. ///// </summary>
  364. //private bool IsFormFieldChecked(Aspose.Words.Fields.FormField formField)
  365. //{
  366. // try
  367. // {
  368. // if (formField.Type != FormFieldType.CheckBox)
  369. // return false;
  370. // // 在15.12.0版本中,通过检查字段代码来判断复选框状态
  371. // var fieldCode = formField.GetFieldCode() ?? string.Empty;
  372. // // 检查常见的复选框选中标记
  373. // return fieldCode.Contains("\\checked", StringComparison.OrdinalIgnoreCase) ||
  374. // fieldCode.Contains("✓", StringComparison.OrdinalIgnoreCase) ||
  375. // fieldCode.Contains("☑", StringComparison.OrdinalIgnoreCase);
  376. // }
  377. // catch
  378. // {
  379. // return false;
  380. // }
  381. //}
  382. ///// <summary>
  383. ///// 获取下拉列表选中的值
  384. ///// </summary>
  385. //private string GetDropDownSelectedValue(Aspose.Words.Fields.FormField formField)
  386. //{
  387. // try
  388. // {
  389. // if (formField.Type != FormFieldType.DropDown)
  390. // return string.Empty;
  391. // // 在15.12.0中,可能需要通过解析字段代码来获取选中的值
  392. // var fieldCode = formField.GetFieldCode() ?? string.Empty;
  393. // // 简单的解析逻辑 - 实际应用中可能需要更复杂的解析
  394. // if (fieldCode.Contains("\\s", StringComparison.OrdinalIgnoreCase))
  395. // {
  396. // var match = System.Text.RegularExpressions.Regex.Match(
  397. // fieldCode,
  398. // @"\\s\s*""([^""]*)""",
  399. // System.Text.RegularExpressions.RegexOptions.IgnoreCase);
  400. // if (match.Success)
  401. // {
  402. // return match.Groups[1].Value;
  403. // }
  404. // }
  405. // return "未选择";
  406. // }
  407. // catch
  408. // {
  409. // return string.Empty;
  410. // }
  411. //}
  412. ///// <summary>
  413. ///// 获取文本表单字段的值
  414. ///// </summary>
  415. //private string GetTextFormFieldValue(Aspose.Words.Fields.FormField formField)
  416. //{
  417. // try
  418. // {
  419. // if (formField.Type != FormFieldType.Regular)
  420. // return string.Empty;
  421. // // 对于文本字段,尝试获取字段结果文本
  422. // // 在15.12.0中,可能需要遍历字段的子节点
  423. // var result = string.Empty;
  424. // // 尝试获取字段的文本内容
  425. // var fieldNodes = formField.GetChildNodes(NodeType.Any, true);
  426. // foreach (Aspose.Words.Node node in fieldNodes)
  427. // {
  428. // if (node.NodeType == NodeType.Run)
  429. // {
  430. // result += ((Run)node).Text;
  431. // }
  432. // }
  433. // return result.Trim();
  434. // }
  435. // catch
  436. // {
  437. // return string.Empty;
  438. // }
  439. //}
  440. ///// <summary>
  441. ///// 获取表单字段状态
  442. ///// </summary>
  443. //private string GetFormFieldStatus(Aspose.Words.Fields.FormField formField)
  444. //{
  445. // try
  446. // {
  447. // var fieldCode = formField.GetFieldCode() ?? string.Empty;
  448. // if (fieldCode.Contains("\\locked", StringComparison.OrdinalIgnoreCase))
  449. // return "已锁定";
  450. // if (fieldCode.Contains("\\disabled", StringComparison.OrdinalIgnoreCase))
  451. // return "已禁用";
  452. // return "活动";
  453. // }
  454. // catch
  455. // {
  456. // return "未知";
  457. // }
  458. //}
  459. ///// <summary>
  460. ///// 获取表单字段默认值
  461. ///// </summary>
  462. //private string GetFormFieldDefaultValue(Aspose.Words.Fields.FormField formField)
  463. //{
  464. // try
  465. // {
  466. // var fieldCode = formField.GetFieldCode() ?? string.Empty;
  467. // // 解析默认值
  468. // var match = System.Text.RegularExpressions.Regex.Match(
  469. // fieldCode,
  470. // @"\\d\s*""([^""]*)""",
  471. // System.Text.RegularExpressions.RegexOptions.IgnoreCase);
  472. // if (match.Success)
  473. // {
  474. // return match.Groups[1].Value;
  475. // }
  476. // return string.Empty;
  477. // }
  478. // catch
  479. // {
  480. // return string.Empty;
  481. // }
  482. //}
  483. ///// <summary>
  484. ///// 获取表单字段最大长度
  485. ///// </summary>
  486. //private int GetFormFieldMaxLength(Aspose.Words.Fields.FormField formField)
  487. //{
  488. // try
  489. // {
  490. // var fieldCode = formField.GetFieldCode() ?? string.Empty;
  491. // var match = System.Text.RegularExpressions.Regex.Match(
  492. // fieldCode,
  493. // @"\\l\s*(\d+)",
  494. // System.Text.RegularExpressions.RegexOptions.IgnoreCase);
  495. // if (match.Success && int.TryParse(match.Groups[1].Value, out int maxLength))
  496. // {
  497. // return maxLength;
  498. // }
  499. // return 0;
  500. // }
  501. // catch
  502. // {
  503. // return 0;
  504. // }
  505. //}
  506. ///// <summary>
  507. ///// 获取下拉列表选项
  508. ///// </summary>
  509. //private List<string> GetFormFieldOptions(Aspose.Words.Fields.FormField formField)
  510. //{
  511. // var options = new List<string>();
  512. // try
  513. // {
  514. // if (formField.Type != FormFieldType.DropDown)
  515. // return options;
  516. // var fieldCode = formField.GetFieldCode() ?? string.Empty;
  517. // // 解析下拉选项
  518. // var matches = System.Text.RegularExpressions.Regex.Matches(
  519. // fieldCode,
  520. // @"""([^""]*)""",
  521. // System.Text.RegularExpressions.RegexOptions.IgnoreCase);
  522. // foreach (System.Text.RegularExpressions.Match match in matches)
  523. // {
  524. // if (match.Success)
  525. // {
  526. // options.Add(match.Groups[1].Value);
  527. // }
  528. // }
  529. // return options;
  530. // }
  531. // catch
  532. // {
  533. // return options;
  534. // }
  535. ///// <summary>
  536. ///// 统计图片数量
  537. ///// </summary>
  538. //private int CountImages(Document doc)
  539. //{
  540. // try
  541. // {
  542. // int imageCount = 0;
  543. // foreach (Aspose.Words.Drawing.Shape shape in doc.GetChildNodes(NodeType.Shape, true))
  544. // {
  545. // if (shape.HasImage)
  546. // {
  547. // imageCount++;
  548. // }
  549. // }
  550. // return imageCount;
  551. // }
  552. // catch (Exception ex)
  553. // {
  554. // _logger.LogWarning(ex, "统计图片数量失败");
  555. // return 0;
  556. // }
  557. //}
  558. ///// <summary>
  559. ///// 检查文件是否为支持的Word格式
  560. ///// </summary>
  561. //public bool IsSupportedWordFormat(string fileName)
  562. //{
  563. // var extension = Path.GetExtension(fileName).ToLower();
  564. // return extension switch
  565. // {
  566. // ".doc" => true,
  567. // ".docx" => true,
  568. // ".dot" => true,
  569. // ".dotx" => true,
  570. // ".docm" => true,
  571. // ".dotm" => true,
  572. // _ => false
  573. // };
  574. //}
  575. }
  576. }