using System; using System.Collections; using System.Windows.Forms; //using Excel = Microsoft.Office.Interop.Excel; using System; using System.Data; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Collections.Generic; using System.Threading.Tasks; namespace PTMedicalInsurance.Common { public class ExportToExcel { /// /// 将DataTable导出为CSV文件(完全替代原有的Excel导出) /// public static string DataTabletoCSV(DataTable tmpDataTable, string strFileName) { // 1. 前置校验:空值和路径合法性检查 if (tmpDataTable == null || tmpDataTable.Rows.Count == 0) { MessageBox.Show("无导出数据!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return string.Empty; } // 校验并处理导出路径 if (!ValidateAndFixExportPath(ref strFileName)) { return string.Empty; } // 确保扩展名为.csv strFileName = EnsureCsvExtension(strFileName); FileStream fs = null; StreamWriter sw = null; StringBuilder sb = new StringBuilder(65536); // 64KB缓冲区 try { int rowCount = tmpDataTable.Rows.Count; int columnCount = tmpDataTable.Columns.Count; const int batchSize = 5000; // 每批处理5000行 // 2. 创建文件流 fs = new FileStream(strFileName, FileMode.Create, FileAccess.Write, FileShare.None); // 写入UTF-8 BOM,确保Excel打开不乱码 fs.WriteByte(0xEF); fs.WriteByte(0xBB); fs.WriteByte(0xBF); sw = new StreamWriter(fs, Encoding.UTF8); // 3. 写入表头 List headers = new List(); for (int i = 0; i < columnCount; i++) { string headerName = GetColumnNameMapping(tmpDataTable.Columns[i].ColumnName); headers.Add(EscapeCsvValue(headerName)); } sw.WriteLine(string.Join(",", headers)); // 4. 分批写入数据 for (int r = 0; r < rowCount; r++) { // 每批处理完成后写入文件 if (r > 0 && r % batchSize == 0) { sw.Write(sb.ToString()); sb.Clear(); sw.Flush(); Application.DoEvents(); // 防止UI卡死 } // 构建当前行 for (int i = 0; i < columnCount; i++) { object cellValue = tmpDataTable.Rows[r][i]; // 格式化单元格值 if (cellValue == null || cellValue == DBNull.Value) { sb.Append(string.Empty); } else { string value = cellValue.ToString().Trim(); // 特殊处理:如果是数字类型但需要保留前导零(如身份证号、电话号码) if (IsNumeric(value)) { // 检查是否是需要保留前导零的字段 string columnName = tmpDataTable.Columns[i].ColumnName; if (IsIdNumberField(columnName)) { // 身份证号、电话号码等字段,强制作为文本处理 sb.Append(EscapeCsvValue(value)); } else { sb.Append(value); } } else { sb.Append(EscapeCsvValue(value)); } } if (i < columnCount - 1) { sb.Append(','); } } sb.AppendLine(); } // 5. 写入剩余数据 if (sb.Length > 0) { sw.Write(sb.ToString()); } sw.Flush(); // 6. 导出成功提示 DialogResult result = MessageBox.Show( $"CSV导出成功!\n文件路径:{strFileName}\n总行数:{rowCount}\n\n是否立即打开文件?", "导出成功", MessageBoxButtons.YesNo, MessageBoxIcon.Information); if (result == DialogResult.Yes) { try { System.Diagnostics.Process.Start(strFileName); } catch (Exception ex) { MessageBox.Show($"打开文件失败:{ex.Message}", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } return strFileName; } catch (UnauthorizedAccessException ex) { MessageBox.Show($"导出失败:权限不足!请检查文件路径是否可写。\n详情:{ex.Message}", "权限异常", MessageBoxButtons.OK, MessageBoxIcon.Error); return string.Empty; } catch (IOException ex) { MessageBox.Show($"导出失败:文件读写错误。\n详情:{ex.Message}", "IO异常", MessageBoxButtons.OK, MessageBoxIcon.Error); return string.Empty; } catch (Exception ex) { MessageBox.Show($"导出异常:{ex.Message}\n堆栈信息:{ex.StackTrace}", "导出异常", MessageBoxButtons.OK, MessageBoxIcon.Error); return string.Empty; } finally { // 7. 确保资源释放 if (sw != null) { try { sw.Close(); sw.Dispose(); } catch { } } if (fs != null) { try { fs.Close(); fs.Dispose(); } catch { } } sb.Clear(); GC.Collect(); } } /// /// 杀掉不死进程 /// public void killProcessThread() { ArrayList myProcess = new ArrayList(); for (int i = 0; i < myProcess.Count; i++) { try { System.Diagnostics.Process.GetProcessById(int.Parse((string)myProcess[i])).Kill(); } catch { } } } #region 辅助方法 /// /// 校验并修复导出路径 /// private static bool ValidateAndFixExportPath(ref string filePath) { try { // 1. 检查路径是否为空 if (string.IsNullOrWhiteSpace(filePath)) { filePath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.Desktop), $"导出数据_{DateTime.Now:yyyyMMddHHmmss}.csv"); } // 2. 检查目录是否存在 string directory = Path.GetDirectoryName(filePath); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // 3. 检查文件是否被占用 if (File.Exists(filePath)) { try { using (FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Write, FileShare.None)) { // 文件可写,直接返回 } } catch { DialogResult result = MessageBox.Show($"文件【{filePath}】已被占用,是否覆盖?", "文件占用", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result == DialogResult.No) { return false; } // 尝试删除 try { File.Delete(filePath); } catch { } } } return true; } catch (Exception ex) { MessageBox.Show($"路径校验失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } } /// /// 确保文件扩展名为.csv /// private static string EnsureCsvExtension(string filePath) { string extension = Path.GetExtension(filePath)?.ToLower(); if (extension != ".csv") { return Path.ChangeExtension(filePath, ".csv"); } return filePath; } /// /// 列名映射(保留原有的映射逻辑) /// private static string GetColumnNameMapping(string originalName) { var columnMapping = new Dictionary { { "Adm_Dr", "HIS就诊号" }, { "MdtrtID", "医保住院号" }, { "PatientName", "姓名" }, { "Age", "年龄" }, { "Gend", "性别" }, { "PsnCertNO", "身份证号" }, { "EmpName", "单位或地址" }, { "PsnType", "医保类型" }, { "AdmInDepName", "病人部门名称" }, { "AdmBed", "床号" }, { "MainDiagName", "入院诊断" }, { "AttendDoctorNO", "床位医生" }, { "ChiefPhyDocName", "门诊医生" }, { "OpertName", "入院登记人姓名" }, { "RegDate", "入院登记时间" }, { "insuplcAdmdvs", "参保地区划" }, { "InHospNo", "HIS住院号" }, { "Address", "现住址" } }; return columnMapping.TryGetValue(originalName, out string newName) ? newName : originalName; } /// /// 判断是否为身份证号等需要保留前导零的字段 /// private static bool IsIdNumberField(string columnName) { string lowerName = columnName.ToLower(); return lowerName.Contains("id") || lowerName.Contains("cert") || lowerName.Contains("身份证") || lowerName.Contains("电话") || lowerName.Contains("phone") || lowerName.Contains("code") || lowerName.Contains("编码"); } /// /// 判断是否为数字 /// private static bool IsNumeric(string str) { return double.TryParse(str, out _); } /// /// 转义CSV值中的特殊字符 /// private static string EscapeCsvValue(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } // 检查是否包含特殊字符 bool containsSpecialChar = value.Contains(",") || value.Contains("\"") || value.Contains("\r") || value.Contains("\n") || value.Contains("\t"); if (containsSpecialChar) { // 双引号转义 value = value.Replace("\"", "\"\""); return $"\"{value}\""; } return value; } #endregion } }