ExcelExtension.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. using OfficeOpenXml;
  2. using OfficeOpenXml.Drawing;
  3. using SixLabors.ImageSharp;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Data;
  7. using System.Drawing;
  8. using System.Drawing.Imaging;
  9. using System.IO;
  10. using System.Linq;
  11. using System.Text;
  12. using Image = SixLabors.ImageSharp.Image;
  13. namespace Masuit.Tools.Excel
  14. {
  15. public static class ExcelExtension
  16. {
  17. static ExcelExtension()
  18. {
  19. ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
  20. }
  21. /// <summary>
  22. /// 将内存表自动填充到Excel
  23. /// </summary>
  24. /// <param name="sheetTables">sheet名和内存表的映射</param>
  25. /// <param name="password">密码</param>
  26. /// <returns>内存流</returns>
  27. public static MemoryStream ToExcel(this Dictionary<string, DataTable> sheetTables, string password = null, ColumnSettings settings = null)
  28. {
  29. using var pkg = new ExcelPackage();
  30. foreach (var pair in sheetTables)
  31. {
  32. pair.Value.TableName = pair.Key;
  33. CreateWorksheet(pkg, pair.Value, settings);
  34. }
  35. return SaveAsStream(pkg, password);
  36. }
  37. /// <summary>
  38. /// 将内存表自动填充到Excel
  39. /// </summary>
  40. /// <param name="tables">内存表</param>
  41. /// <param name="password">密码</param>
  42. /// <returns>内存流</returns>
  43. public static MemoryStream ToExcel(this List<DataTable> tables, string password = null, ColumnSettings settings = null)
  44. {
  45. using var pkg = new ExcelPackage();
  46. foreach (var table in tables)
  47. {
  48. CreateWorksheet(pkg, table, settings);
  49. }
  50. return SaveAsStream(pkg, password);
  51. }
  52. /// <summary>
  53. /// 将内存表自动填充到Excel
  54. /// </summary>
  55. /// <param name="table">内存表</param>
  56. /// <param name="password">密码</param>
  57. /// <returns>内存流</returns>
  58. public static MemoryStream ToExcel(this DataTable table, string password = null, ColumnSettings settings = null)
  59. {
  60. using var pkg = new ExcelPackage();
  61. CreateWorksheet(pkg, table, settings);
  62. return SaveAsStream(pkg, password);
  63. }
  64. private static MemoryStream SaveAsStream(ExcelPackage pkg, string password)
  65. {
  66. var ms = new MemoryStream();
  67. if (!string.IsNullOrEmpty(password))
  68. {
  69. pkg.SaveAs(ms, password);
  70. }
  71. else
  72. {
  73. pkg.SaveAs(ms);
  74. }
  75. return ms;
  76. }
  77. public static void CreateWorksheet(this ExcelPackage pkg, DataTable table, ColumnSettings settings = null)
  78. {
  79. if (string.IsNullOrEmpty(table.TableName))
  80. {
  81. table.TableName = "Sheet1";
  82. }
  83. pkg.Workbook.Worksheets.Add(table.TableName);
  84. var sheet = pkg.Workbook.Worksheets[table.TableName];
  85. FillWorksheet(sheet, table, settings);
  86. //打印方向:纵向
  87. sheet.PrinterSettings.Orientation = eOrientation.Landscape;
  88. //集中在一页里打印
  89. sheet.PrinterSettings.FitToPage = true;
  90. //使用A4纸
  91. sheet.PrinterSettings.PaperSize = ePaperSize.A4;
  92. }
  93. /// <summary>
  94. /// 从datatable填充工作簿
  95. /// </summary>
  96. /// <param name="sheet">工作簿</param>
  97. /// <param name="table">数据</param>
  98. /// <param name="settings">列设置</param>
  99. /// <param name="startRow">起始行,默认第一行</param>
  100. /// <param name="startColumn">起始列,默认第一列A列</param>
  101. public static void FillWorksheet(this ExcelWorksheet sheet, DataTable table, ColumnSettings settings = null, int startRow = 1, int startColumn = 1)
  102. {
  103. // 填充表头
  104. var maxWidth = new int[table.Columns.Count];
  105. for (var j = 0; j < table.Columns.Count; j++)
  106. {
  107. sheet.SetValue(startRow, j + startColumn, table.Columns[j].ColumnName);
  108. maxWidth[j] = Encoding.UTF8.GetBytes(table.Columns[j].ColumnName).Length;
  109. }
  110. sheet.Row(startRow).Style.Font.Bold = true; // 表头设置为粗体
  111. sheet.Row(startRow).Style.Font.Size = sheet.Row(startRow).Style.Font.Size * 1.11f; // 表头字号放大1.11倍
  112. sheet.Row(startRow).CustomHeight = true; // 自动调整行高
  113. sheet.Cells.AutoFitColumns(); // 表头自适应列宽
  114. sheet.Cells.Style.WrapText = true;
  115. if (settings != null)
  116. {
  117. foreach (var x in settings.ColumnTypes)
  118. {
  119. sheet.Column(x.Key).Style.Numberformat.Format = x.Value;
  120. }
  121. }
  122. // 填充内容
  123. for (var i = 0; i < table.Rows.Count; i++)
  124. {
  125. sheet.Row(i + startRow + 1).CustomHeight = true; // 自动调整行高
  126. for (var j = 0; j < table.Columns.Count; j++)
  127. {
  128. switch (table.Rows[i][j])
  129. {
  130. case Stream s:
  131. {
  132. if (s.Length > 2)
  133. {
  134. var (pictureType, ms) = Detect(s);
  135. var bmp = new ExcelImage(ms, pictureType).Bounds;
  136. var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), ms, pictureType);
  137. picture.SetPosition(i + startRow, 3, j + startColumn - 1, 5); //设置图片显示位置
  138. var percent = Math.Min(11000f / bmp.Height, 100);
  139. picture.SetSize((int)percent);
  140. sheet.Row(i + startRow + 1).Height = 90;
  141. sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, bmp.Width * percent / 600 > 32 ? bmp.Width * percent / 600 : 32);
  142. }
  143. sheet.SetValue(i + startRow + 1, j + startColumn, "");
  144. break;
  145. }
  146. case IEnumerable<Stream> streams:
  147. {
  148. double sumWidth = 0;
  149. foreach (var stream in streams.Where(stream => stream.Length > 2))
  150. {
  151. var (pictureType, ms) = Detect(stream);
  152. var bmp = new ExcelImage(ms, pictureType).Bounds;
  153. var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), ms, pictureType);
  154. picture.SetPosition(i + startRow, 3, j + startColumn - 1, (int)(5 + sumWidth)); //设置图片显示位置
  155. var percent = Math.Min(11000f / bmp.Height, 100);
  156. picture.SetSize((int)percent);
  157. sheet.Row(i + startRow + 1).Height = 90;
  158. sumWidth += bmp.Width * 1.0 * percent / 100 + 5;
  159. sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth / 6 > 32 ? sumWidth / 6 : 32);
  160. }
  161. sheet.SetValue(i + startRow + 1, j + startColumn, "");
  162. break;
  163. }
  164. case IDictionary<string, Stream> dic:
  165. {
  166. double sumWidth = 0;
  167. foreach (var kv in dic.Where(kv => kv.Value.Length > 2))
  168. {
  169. var (pictureType, ms) = Detect(kv.Value);
  170. var bmp = new ExcelImage(ms, pictureType).Bounds;
  171. var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), ms, pictureType, new Uri(kv.Key));
  172. picture.SetPosition(i + startRow, 3, j + startColumn - 1, (int)(5 + sumWidth)); //设置图片显示位置
  173. var percent = Math.Min(11000f / bmp.Height, 100);
  174. picture.SetSize((int)percent);
  175. sheet.Row(i + startRow + 1).Height = 90;
  176. sumWidth += bmp.Width * 1.0 * percent / 100 + 5;
  177. sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth / 6 > 32 ? sumWidth / 6 : 32);
  178. }
  179. sheet.SetValue(i + startRow + 1, j + startColumn, "");
  180. break;
  181. }
  182. case IDictionary<string, MemoryStream> dic:
  183. {
  184. double sumWidth = 0;
  185. foreach (var kv in dic.Where(kv => kv.Value.Length > 2))
  186. {
  187. var (pictureType, ms) = Detect(kv.Value);
  188. var bmp = new ExcelImage(ms, pictureType).Bounds;
  189. var picture = sheet.Drawings.AddPicture(Guid.NewGuid().ToString(), ms, pictureType, new Uri(kv.Key));
  190. picture.SetPosition(i + startRow, 3, j + startColumn - 1, (int)(5 + sumWidth)); //设置图片显示位置
  191. var percent = Math.Min(11000f / bmp.Height, 100);
  192. picture.SetSize((int)percent);
  193. sheet.Row(i + startRow + 1).Height = 90;
  194. sumWidth += bmp.Width * 1.0 * percent / 100 + 5;
  195. sheet.Column(j + startColumn).Width = Math.Max(sheet.Column(j + startColumn).Width, sumWidth / 6 > 32 ? sumWidth / 6 : 32);
  196. }
  197. sheet.SetValue(i + startRow + 1, j + startColumn, "");
  198. break;
  199. }
  200. default:
  201. {
  202. sheet.SetValue(i + startRow + 1, j + startColumn, table.Rows[i][j] ?? "");
  203. if (table.Rows[i][j] is ValueType)
  204. {
  205. sheet.Column(j + startColumn).AutoFit();
  206. }
  207. else
  208. {
  209. // 根据单元格内容长度来自适应调整列宽
  210. sheet.Column(j + startColumn).Width = Math.Max(Encoding.UTF8.GetBytes(table.Rows[i][j].ToString() ?? string.Empty).Length + 2, sheet.Column(j + startColumn).Width);
  211. if (sheet.Column(j + startColumn).Width > 110)
  212. {
  213. sheet.Column(j + startColumn).AutoFit(100, 110);
  214. }
  215. }
  216. break;
  217. }
  218. }
  219. }
  220. }
  221. }
  222. private static readonly NumberFormater NumberFormater = new("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1);
  223. /// <summary>
  224. /// 检测图片格式
  225. /// </summary>
  226. /// <param name="stream"></param>
  227. /// <returns></returns>
  228. private static (ePictureType type, Stream stream) Detect(Stream stream)
  229. {
  230. try
  231. {
  232. var pictureType = Image.DetectFormat(stream).Name switch
  233. {
  234. "JPEG" => ePictureType.Jpg,
  235. "PNG" => ePictureType.Png,
  236. "GIF" => ePictureType.Gif,
  237. "BMP" => ePictureType.Bmp,
  238. "SVG" => ePictureType.Svg,
  239. "TIF" => ePictureType.Tif,
  240. "WEBP" => ePictureType.WebP,
  241. _ => default
  242. };
  243. return (pictureType, stream);
  244. }
  245. catch
  246. {
  247. try
  248. {
  249. using var image = Image.Load(stream);
  250. var ms = new MemoryStream();
  251. image.SaveAsWebp(ms);
  252. stream.Dispose();
  253. return (ePictureType.WebP, ms);
  254. }
  255. catch
  256. {
  257. using var bmp = new Bitmap(stream);
  258. var ms = new MemoryStream();
  259. bmp.Save(ms, ImageFormat.Jpeg);
  260. stream.Dispose();
  261. return (ePictureType.Jpg, ms);
  262. }
  263. }
  264. }
  265. /// <summary>
  266. /// 获取字母列
  267. /// </summary>
  268. /// <param name="sheet"></param>
  269. /// <param name="index"></param>
  270. /// <returns></returns>
  271. public static ExcelColumn Column(this ExcelWorksheet sheet, string index)
  272. {
  273. return sheet.Column((int)NumberFormater.FromString(index));
  274. }
  275. /// <summary>
  276. /// 获取字母列
  277. /// </summary>
  278. /// <param name="sheet"></param>
  279. /// <param name="index"></param>
  280. /// <returns></returns>
  281. public static ExcelColumn Column(this ExcelWorksheet sheet, char index)
  282. {
  283. if (index is < 'A' or > 'Z')
  284. {
  285. throw new ArgumentOutOfRangeException(nameof(index));
  286. }
  287. return sheet.Column(index - 64);
  288. }
  289. }
  290. }