ImportController.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. using Azure.Storage.Blobs.Models;
  2. using Azure.Storage.Sas;
  3. using HTEXLib;
  4. using HTEXLib.Builders;
  5. using HTEXLib.Helpers.ShapeHelpers;
  6. using Microsoft.AspNetCore.Hosting;
  7. using Microsoft.AspNetCore.Http;
  8. using Microsoft.AspNetCore.Mvc;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Globalization;
  12. using System.IdentityModel.Tokens.Jwt;
  13. using System.IO;
  14. using System.Linq;
  15. using System.Net.Http;
  16. using System.Text;
  17. using System.Text.Json;
  18. using System.Threading.Tasks;
  19. using TEAMModelOS.SDK.Models;
  20. using TEAMModelOS.Models.Dto;
  21. using TEAMModelOS.Models.PowerPoint;
  22. using TEAMModelOS.SDK;
  23. using TEAMModelOS.SDK.Context.Constant;
  24. using TEAMModelOS.SDK.Context.Constant.Common;
  25. using TEAMModelOS.SDK.DI;
  26. using TEAMModelOS.SDK.Extension;
  27. using TEAMModelOS.SDK.Module.AzureBlob.Configuration;
  28. using TEAMModelOS.SDK.Module.AzureBlob.Container;
  29. using TEAMModelOS.Services;
  30. using HTEXLib.Translator;
  31. using HTEXLib.DOCX.Models;
  32. using System.Collections.Concurrent;
  33. using TEAMModelOS.Filter;
  34. using Ionic.Zip;
  35. namespace TEAMModelOS.Controllers
  36. {
  37. [Route("import")]
  38. [ApiController]
  39. public class ImportController : ControllerBase
  40. {
  41. public PPTX2HTEXTranslator _PPTX2HTEXTranslator { get; set; }
  42. public DOXC2HTMLTranslator _DOXC2HTMLTranslator { get; set; }
  43. public HTML2ITEMTranslator _HTML2ITEMTranslator { get; set; }
  44. // private readonly IHtexService htexService;
  45. private readonly AzureStorageFactory _azureStorage;
  46. private readonly IWebHostEnvironment _webHostEnvironment;
  47. private List<LangConfig> langConfigs { get; set; }
  48. private readonly IHttpClientFactory _clientFactory;
  49. public ImportController(AzureStorageFactory azureStorage, IWebHostEnvironment webHostEnvironment,
  50. PPTX2HTEXTranslator PPTX2HTEXTranslator, IHttpClientFactory clientFactory,
  51. DOXC2HTMLTranslator DOXC2HTMLTranslator, HTML2ITEMTranslator HTML2ITEMTranslator)
  52. {
  53. _HTML2ITEMTranslator = HTML2ITEMTranslator;
  54. _DOXC2HTMLTranslator = DOXC2HTMLTranslator;
  55. _clientFactory = clientFactory;
  56. this._PPTX2HTEXTranslator = PPTX2HTEXTranslator;
  57. _webHostEnvironment = webHostEnvironment;
  58. _azureStorage = azureStorage;
  59. string path = _webHostEnvironment.ContentRootPath + "/JsonFile/Core/LangConfig.json";
  60. FileStream fs = new FileStream(path, System.IO.FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
  61. StreamReader sr = new StreamReader(fs, System.Text.Encoding.UTF8);
  62. String line;
  63. StringBuilder builder = new StringBuilder();
  64. while ((line = sr.ReadLine()) != null)
  65. {
  66. builder.Append(line.ToString());
  67. }
  68. sr.Close();
  69. string text = builder.ToString();
  70. langConfigs = text.ToObject<List<LangConfig>>();
  71. }
  72. private static string ReplaceLast(string input, string oldValue, string newValue)
  73. {
  74. int index = input.LastIndexOf(oldValue);
  75. if (index < 0)
  76. {
  77. return input;
  78. }
  79. else
  80. {
  81. StringBuilder sb = new StringBuilder(input.Length - oldValue.Length + newValue.Length);
  82. sb.Append(input.Substring(0, index));
  83. sb.Append(newValue);
  84. sb.Append(input.Substring(index + oldValue.Length,
  85. input.Length - index - oldValue.Length));
  86. return sb.ToString();
  87. }
  88. }
  89. /// <summary>
  90. /// {"file":"www....xxxx.pptx","scope":"private/school"}
  91. /// </summary>
  92. /// <param name="request"></param>
  93. /// <returns></returns>
  94. [HttpPost("parse-doc")]
  95. //[RequestSizeLimit(102_400_000_00)] //最大10000m左右
  96. [AuthToken(Roles = "admin,teacher")]
  97. public async Task<IActionResult> ParseDoc(JsonElement request)
  98. {
  99. //string id_token = HttpContext.GetXAuth("IdToken");
  100. //if (string.IsNullOrEmpty(id_token)) return BadRequest();
  101. //var jwt = new JwtSecurityToken(id_token);
  102. //if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  103. //var id = jwt.Payload.Sub;
  104. var (id, _, _, school) = HttpContext.GetAuthTokenInfo();
  105. var containerid = id;
  106. if (request.TryGetProperty("scope", out JsonElement jscope))
  107. {
  108. if (jscope.GetString().Equals("school"))
  109. {
  110. containerid = school;
  111. }
  112. }
  113. request.TryGetProperty("file", out JsonElement code);
  114. string azureBlobSAS = System.Web.HttpUtility.UrlDecode(code.ToString(), Encoding.UTF8);
  115. (string, string) a = BlobUrlString(azureBlobSAS);
  116. string ContainerName = a.Item1;
  117. string BlobName = a.Item2;
  118. bool flg = IsBlobName(BlobName);
  119. var codes = azureBlobSAS.Split("/");
  120. var file = codes[codes.Length - 1].Split(".");
  121. var ext = file[file.Length - 1];
  122. var FileName = ReplaceLast(codes[codes.Length - 1], "." + ext, "");
  123. if (flg)
  124. {
  125. BlobAuth blobAuth = _azureStorage.GetBlobSasUriRead(ContainerName, BlobName);
  126. var response = await _clientFactory.CreateClient().GetAsync(new Uri(blobAuth.url));
  127. response.EnsureSuccessStatusCode();
  128. Stream stream = await response.Content.ReadAsStreamAsync();
  129. if (ext.ToLower() == "pptx" || ext.ToLower() == "xml")
  130. {
  131. string index = await PPTXTranslator(containerid, FileName, stream);
  132. return Ok(new { index = index });
  133. }
  134. else if (ext.ToLower() == "docx" || ext.ToLower() == "doc")
  135. {
  136. return Ok(new { index = "" });
  137. }
  138. else if (ext.ToLower() == "htex" )
  139. {
  140. var index=await HTEXTranslator(containerid, FileName, stream);
  141. return Ok(new { index = index });
  142. }
  143. else {
  144. return Ok(new { index = "" });
  145. }
  146. }
  147. else { return BadRequest("不是正确的Blob链接!"); }
  148. }
  149. private async Task<string> HTEXTranslator(string containerid,string FileName, Stream stream)
  150. {
  151. await _azureStorage.GetBlobServiceClient().DelectBlobs(containerid, $"res/{FileName}");
  152. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  153. //处理中文乱码问题
  154. Encoding encoding = Encoding.GetEncoding("GB2312");
  155. var options = new ReadOptions { Encoding = encoding };
  156. string index=null;
  157. bool hasindex = false;
  158. List<Task<string>> tasks = new List<Task<string>>();
  159. ZipFile zip = ZipFile.Read(stream, options);
  160. zip.AlternateEncoding = encoding;
  161. List<Stream> streams = new List<Stream>();
  162. foreach (var f in zip.Entries)
  163. {
  164. string name = FileName +"/"+ f.FileName;
  165. if (f.IsDirectory)
  166. {
  167. continue;
  168. }
  169. var uploadStream = f.OpenReader();
  170. byte[] buffer = new byte[uploadStream.Length];
  171. uploadStream.Read(buffer, 0, buffer.Length);
  172. Stream blobstream = new MemoryStream(buffer);
  173. streams.Add(blobstream);
  174. tasks.Add( _azureStorage.UploadFileByContainer(containerid, blobstream, "res", $"{name}", false));
  175. if (name.Contains($"{FileName}/index.json")) {
  176. hasindex = true;
  177. }
  178. uploadStream.Close();
  179. }
  180. zip.Dispose();
  181. stream.Close();
  182. if (hasindex) {
  183. await Task.WhenAll(tasks);
  184. foreach (var task in tasks)
  185. {
  186. var url= System.Web.HttpUtility.UrlDecode(task.Result, Encoding.UTF8);
  187. if (url.Contains($"{FileName}/index.json")) {
  188. index = url;
  189. }
  190. }
  191. }
  192. //释放资源
  193. streams.ForEach(x => { x.Close(); });
  194. return index;
  195. }
  196. private static (string, string) BlobUrlString(string sasUrl)
  197. {
  198. sasUrl = sasUrl.Substring(8);
  199. string[] sasUrls = sasUrl.Split("/");
  200. string ContainerName;
  201. ContainerName = sasUrls[1].Clone().ToString();
  202. string item = sasUrls[0] + "/" + sasUrls[1] + "/";
  203. string blob = sasUrl.Replace(item, "");
  204. return (ContainerName, blob);
  205. }
  206. public static bool IsBlobName(string BlobName)
  207. {
  208. return System.Text.RegularExpressions.Regex.IsMatch(BlobName,
  209. @"(?!((^(con)$)|^(con)\\..*|(^(prn)$)|^(prn)\\..*|(^(aux)$)|^(aux)\\..*|(^(nul)$)|^(nul)\\..*|(^(com)[1-9]$)|^(com)[1-9]\\..*|(^(lpt)[1-9]$)|^(lpt)[1-9]\\..*)|^\\s+|.*\\s$)(^[^\\\\\\:\\<\\>\\*\\?\\\\\\""\\\\|]{1,255}$)");
  210. }
  211. /// <summary>
  212. ///
  213. /// </summary>
  214. /// <param name="request"></param>
  215. /// <returns></returns>
  216. [HttpPost("upload-pptx")]
  217. [RequestSizeLimit(102_400_000_00)] //最大10000m左右
  218. public async Task<IActionResult> UploadPPTX([FromForm] IFormFile file)
  219. {
  220. var (id, _, _, school) = HttpContext.GetAuthTokenInfo();
  221. if (FileType.GetExtention(file.FileName).ToLower().Equals("pptx") || FileType.GetExtention(file.FileName).ToLower().Equals("xml"))
  222. {
  223. string FileName = file.FileName.Split(".")[0];
  224. Stream streamFile = file.OpenReadStream();
  225. string index = await PPTXTranslator(id, FileName, streamFile);
  226. return Ok(new { index = index });
  227. }
  228. else
  229. {
  230. return BadRequest("type is not pptx or xml !");
  231. }
  232. }
  233. /// <summary>
  234. /// docUrl
  235. /// folder
  236. /// shaCode
  237. ///
  238. /// UploadWord
  239. /// </summary>
  240. /// <param name="request"></param>
  241. /// <returns></returns>
  242. [HttpPost("upload-word")]
  243. [RequestSizeLimit(102_400_000_00)] //最大10000m左右
  244. public IActionResult UploadWord([FromForm] IFormFile file)
  245. {
  246. // ResponseBuilder responseBuilder = new ResponseBuilder();
  247. if (!FileType.GetExtention(file.FileName).ToLower().Equals("docx"))
  248. {
  249. return BadRequest(new Dictionary<string, object> { { "msg", "type is not docx!" }, { "code", ResponseCode.FAILED } });
  250. }
  251. var doc = _DOXC2HTMLTranslator.Translate(file.OpenReadStream());
  252. // Dictionary<string, object> model = await ImportExerciseService.UploadWord(_azureStorage, file);
  253. return Ok(doc);
  254. }
  255. /// <summary>
  256. /// htmlString AnalyzeHtml
  257. /// </summary>
  258. /// <param name="request"></param>
  259. /// <returns></returns>
  260. [HttpPost("parse-html")]
  261. public IActionResult AnalyzeHtml(JsonElement request)
  262. {
  263. //ResponseBuilder builder = ResponseBuilder.custom();
  264. Dictionary<string, object> dict = new Dictionary<string, object>();
  265. var emobj = request.EnumerateObject();
  266. while (emobj.MoveNext())
  267. {
  268. dict[emobj.Current.Name] = emobj.Current.Value;
  269. }
  270. bool flag = dict.TryGetValue("htmlString", out object htmlString);
  271. bool flagLang = dict.TryGetValue("lang", out object lang);
  272. if (flag && htmlString != null && !string.IsNullOrEmpty(htmlString.ToString()))
  273. {
  274. LangConfig langConfig = langConfigs.Where(x => x.Lang == lang.ToString()).FirstOrDefault();
  275. var exercises = _HTML2ITEMTranslator.Translate(htmlString.ToString(), langConfig);
  276. return Ok(exercises);
  277. }
  278. else
  279. {
  280. return BadRequest();
  281. }
  282. }
  283. private async Task<string> PPTXTranslator(string containerid, string FileName, Stream streamFile)
  284. {
  285. if (string.IsNullOrWhiteSpace(containerid))
  286. {
  287. containerid = "teammodelos";
  288. }
  289. var status = await _azureStorage.GetBlobServiceClient().DelectBlobs(containerid, $"res/{FileName}");
  290. string shaCode = Guid.NewGuid().ToString();
  291. HTEXLib.Htex htex = _PPTX2HTEXTranslator.Translate(streamFile);
  292. htex.name = FileName;
  293. var slides = htex.slides;
  294. List<Task<string>> tasks = new List<Task<string>>();
  295. HTEXIndex index = new HTEXIndex() { name = FileName, size = htex.size, thumbnail = htex.thumbnail, id = shaCode };
  296. List<KeyValuePair<string, string>> blobslidenames = new List<KeyValuePair<string, string>>();
  297. foreach (var slide in slides)
  298. {
  299. string json = JsonHelper.ToJson(slide, ignoreNullValue: false);
  300. string guid = Guid.NewGuid().ToString();
  301. blobslidenames.Add(new KeyValuePair<string, string>(guid, json));
  302. }
  303. List<Sld> slds = new List<Sld>();
  304. foreach (var key in blobslidenames)
  305. {
  306. slds.Add(new Sld { type = "normal", url = $"{key.Key}.json", scoring = null }); ;
  307. tasks.Add(_azureStorage.UploadFileByContainer(containerid, key.Value, "res", $"{FileName}/{key.Key}.json", false));
  308. }
  309. await Task.WhenAll(tasks);
  310. // Dictionary<string, Store> dict = new Dictionary<string, Store>();
  311. List<Task> tasksFiles = new List<Task>();
  312. foreach (var key in htex.stores.Keys)
  313. {
  314. if (key.EndsWith(".wdp") || key.EndsWith(".xlsx"))
  315. {
  316. htex.stores.Remove(key);
  317. continue;
  318. }
  319. var store = htex.stores[key];
  320. Store str = new Store() { path = key, contentType = store.contentType, isLazy = store.isLazy };
  321. if (!store.isLazy && store.contentType != null && ContentTypeDict.extdict.TryGetValue(store.contentType, out string ext) && store.url.Contains(";base64,"))
  322. {
  323. string[] strs = store.url.Split(',');
  324. Stream stream = new MemoryStream(Convert.FromBase64String(strs[1]));
  325. // var urlstrs = key.Split("/");
  326. var name = key.Replace("/", "");
  327. str.url = $"{name}";
  328. tasksFiles.Add(_azureStorage.UploadFileByContainer(containerid, stream, "res", $"{FileName}/{name}", false));
  329. }
  330. else
  331. {
  332. str.url = System.Web.HttpUtility.UrlDecode(store.url, Encoding.UTF8);
  333. }
  334. // dict.TryAdd(key, str);
  335. }
  336. await Task.WhenAll(tasksFiles);
  337. // index.stores = dict;
  338. index.slides = slds;
  339. var BlobUrl = await _azureStorage.UploadFileByContainer(containerid, JsonHelper.ToJson(index, ignoreNullValue: false), "res", FileName + "/" + "index.json", false);
  340. return System.Web.HttpUtility.UrlDecode(BlobUrl, Encoding.UTF8);
  341. }
  342. }
  343. public class HTEXIndex
  344. {
  345. public string id { get; set; }
  346. public string version { get; set; } = "1.0.20201210";
  347. public string name { get; set; }
  348. public HTEXLib.HtexSize size { get; set; }
  349. public List<Sld> slides { get; set; }
  350. //缩略图
  351. public string thumbnail { get; set; }
  352. // public int page { get; set; }
  353. // public Dictionary<string, Store> stores { get; set; }
  354. public List<string> points { get; set; }
  355. public string periodId { get; set; }
  356. public List<string> gradeIds { get; set; }
  357. public string subjectId { get; set; }
  358. public string subjectName { get; set; }
  359. public string score { get; set; }
  360. public string code { get; set; }
  361. public string scope { get; set; }
  362. public int? multipleRule { get; set; }
  363. }
  364. public class Sld
  365. {
  366. /// <summary>
  367. /// normal,普通的hte页面 single 单选题 multiple 多选题 judge 判断题 complete 填空题 subjective 问答题 compose 综合题
  368. /// </summary>
  369. public string type { get; set; }
  370. /// <summary>
  371. /// 单页PPTx htex 的解析链接或一个题目的链接
  372. /// </summary>
  373. public string url { get; set; }
  374. /// <summary>
  375. /// 题目的配分,如果为type为normal 及compose ,则 scoring=null
  376. /// </summary>
  377. public Scoring scoring { get; set; }
  378. /// <summary>
  379. /// 单页PPTx htex 的缩略图
  380. /// </summary>
  381. public string thumbnail { get; set; }
  382. }
  383. public class Scoring
  384. {
  385. public double score { get; set; }
  386. public List<string> ans { get; set; } = new List<string>();
  387. }
  388. }