平安校园
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ImportExportService.cs 7.7 KiB

2 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. 
  2. //
  3. namespace SafeCampus.System;
  4. /// <summary>
  5. /// <inheritdoc cref="IImportExportService"/>
  6. /// </summary>
  7. public class ImportExportService : IImportExportService
  8. {
  9. #region 导入
  10. /// <inheritdoc/>
  11. public void ImportVerification(IFormFile file, int maxSize = 30, string[] allowTypes = null)
  12. {
  13. if (file == null) throw Oops.Bah("文件不能为空");
  14. if (file.Length > maxSize * 1024 * 1024) throw Oops.Bah($"文件大小不允许超过{maxSize}M");
  15. var fileSuffix = Path.GetExtension(file.FileName).ToLower().Split(".")[1];// 文件后缀
  16. var allowTypeS = allowTypes == null ? new[] { "xlsx" } : allowTypes;//允许上传的文件类型
  17. if (!allowTypeS.Contains(fileSuffix)) throw Oops.Bah(errorMessage: "文件格式错误");
  18. }
  19. /// <inheritdoc/>
  20. public ImportPreviewOutput<T> TemplateDataVerification<T>(ImportResult<T> importResult) where T : ImportTemplateInput
  21. {
  22. if (importResult.Exception != null) throw Oops.Bah("导入异常,请检查文件格式!");
  23. ////遍历模板错误
  24. importResult.TemplateErrors.ForEach(error =>
  25. {
  26. if (error.Message.Contains("not found")) throw Oops.Bah($"列[{error.RequireColumnName}]未找到");
  27. throw Oops.Bah($"列[{error.RequireColumnName}]:{error.Message}");
  28. });
  29. if (importResult.Data == null)
  30. throw Oops.Bah("文件数据格式有误,请重新导入!");
  31. //导入结果输出
  32. var importPreview = new ImportPreviewOutput<T> { HasError = importResult.HasError };
  33. var headerMap = new Dictionary<string, string>();
  34. //遍历导入的表头列表信息
  35. importResult.ImporterHeaderInfos.ForEach(it =>
  36. {
  37. headerMap.Add(it.Header.Name, it.PropertyName);
  38. var tableColumns = new TableColumns
  39. { Title = it.Header.Name.Split("(")[0], DataIndex = it.PropertyName.FirstCharToLower() };//定义表头,部分表头有说明用(分组去掉说明
  40. var antTableAttribute = it.PropertyInfo.GetCustomAttribute<AntTableAttribute>();//获取表格特性
  41. if (antTableAttribute != null)
  42. {
  43. tableColumns.Date = antTableAttribute.IsDate;
  44. tableColumns.Ellipsis = antTableAttribute.Ellipsis;
  45. tableColumns.Width = antTableAttribute.Width;
  46. }
  47. importPreview.TableColumns.Add(tableColumns);//添加到表头
  48. });
  49. //导入的数据转集合
  50. var data = importResult.Data.ToList();
  51. var systemError = new string[] { };//系统错误提示
  52. //遍历错误列,将错误字典中的中文改成英文跟实体对应
  53. importResult.RowErrors.ForEach(row =>
  54. {
  55. IDictionary<string, string> fieldErrors = new Dictionary<string, string>();//定义字典
  56. //遍历错误列,赋值给新的字典
  57. row.FieldErrors.ForEach(it =>
  58. {
  59. var errrVaule = it.Value;
  60. //value xx Invalid, please fill in the correct integer value!
  61. //value xx Invalid, please fill in the correct date and time format!
  62. if (it.Value.Contains("Invalid"))//如果错误信息有Invalid就提示格式错误
  63. errrVaule = $"{it.Key}格式错误";
  64. fieldErrors.Add(headerMap[it.Key], errrVaule);
  65. });
  66. row.FieldErrors = fieldErrors;//替换新的字典
  67. row.RowIndex -= 2;//下表与列表中的下标一致
  68. data[row.RowIndex].HasError = true;//错误的行HasError = true
  69. data[row.RowIndex].ErrorInfo = fieldErrors;//替换新的字典
  70. });
  71. data = data.OrderByDescending(it => it.HasError).ToList();//排序
  72. importPreview.Data = data;//重新赋值data
  73. return importPreview;
  74. }
  75. /// <inheritdoc/>
  76. public async Task<FileStreamResult> GenerateTemplate<T>(string fileName) where T : class, new()
  77. {
  78. IImporter importer = new ExcelImporter();
  79. var byteArray = await importer.GenerateTemplateBytes<T>();
  80. var result = GetFileStreamResult(byteArray, fileName);
  81. return result;
  82. }
  83. /// <inheritdoc/>
  84. public FileStreamResult GenerateLocalTemplate(string fileName, string templateFolder = "Template")
  85. {
  86. var folder = App.WebHostEnvironment.WebRootPath.CombinePath(templateFolder);
  87. return GetFileStreamResult(folder, fileName, true);
  88. }
  89. /// <inheritdoc/>
  90. public async Task<ImportPreviewOutput<T>> GetImportPreview<T>(IFormFile file) where T : ImportTemplateInput, new()
  91. {
  92. ImportVerification(file);//验证文件
  93. IImporter importer = new ExcelImporter();
  94. using var fileStream = file.OpenReadStream();//获取文件流
  95. var import = await importer.Import<T>(fileStream);//导入的文件转化为带入结果
  96. var importPreview = TemplateDataVerification(import);//验证数据完整度
  97. return importPreview;
  98. }
  99. /// <inheritdoc/>
  100. public ImportResultOutPut<T> GetImportResultPreview<T>(List<T> data, out List<T> importData) where T : ImportTemplateInput
  101. {
  102. //定义结果
  103. var result = new ImportResultOutPut<T> { Total = data.Count };
  104. //可以导入的数据
  105. importData = data.Where(it => it.HasError == false).ToList();
  106. //如果有错误
  107. if (importData.Count != data.Count)
  108. {
  109. result.Success = false;
  110. result.Data = data.Where(it => it.HasError).ToList();
  111. result.FailCount = data.Count - importData.Count;
  112. }
  113. result.ImportCount = importData.Count;
  114. return result;
  115. }
  116. #endregion 导入
  117. #region 导出
  118. public async Task<FileStreamResult> Export<T>(List<T> data, string fileName) where T : class, new()
  119. {
  120. IExporter exporter = new ExcelExporter();
  121. var byteArray = await exporter.ExportAsByteArray(data);
  122. var result = GetFileStreamResult(byteArray, fileName);
  123. return result;
  124. }
  125. #endregion 导出
  126. #region 方法
  127. /// <summary>
  128. /// 获取文件流
  129. /// </summary>
  130. /// <param name="path"></param>
  131. /// <param name="fileName"></param>
  132. /// <param name="isPathFolder"></param>
  133. /// <returns></returns>
  134. public FileStreamResult GetFileStreamResult(string path, string fileName, bool isPathFolder = false)
  135. {
  136. fileName = GetFileName(fileName);
  137. if (isPathFolder) path = path.CombinePath(fileName);
  138. //文件转流
  139. var result = new FileStreamResult(new FileStream(path, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName };
  140. return result;
  141. }
  142. /// <summary>
  143. /// 获取文件流
  144. /// </summary>
  145. /// <param name="byteArray"></param>
  146. /// <param name="fileName"></param>
  147. /// <returns></returns>
  148. public FileStreamResult GetFileStreamResult(byte[] byteArray, string fileName)
  149. {
  150. fileName = GetFileName(fileName);
  151. //文件转流
  152. var result = new FileStreamResult(new MemoryStream(byteArray), "application/octet-stream") { FileDownloadName = fileName };
  153. return result;
  154. }
  155. /// <summary>
  156. /// 获取文件名
  157. /// </summary>
  158. /// <param name="fileName"></param>
  159. /// <returns></returns>
  160. public string GetFileName(string fileName)
  161. {
  162. if (!fileName.Contains("."))
  163. fileName = fileName + ".xlsx";
  164. fileName = HttpUtility.UrlEncode(fileName, Encoding.GetEncoding("UTF-8"));//文件名转utf8不然前端下载会乱码
  165. return fileName;
  166. }
  167. /// <summary>
  168. /// 获取本地模板路径
  169. /// </summary>
  170. /// <returns></returns>
  171. public string GetTemplateFolder()
  172. {
  173. var folder = App.WebHostEnvironment.WebRootPath.CombinePath("Template");
  174. return folder;
  175. }
  176. #endregion 方法
  177. }