.NET C#无第三方包实现Excel转CSV:上传后转码异常求助
问题根源
你的ConvertExcelToCsv方法逻辑完全错误:Excel文件(不管是.xlsx还是.xls)都是二进制格式,不是纯文本的制表符分隔文件。直接读取文件字节并转换为字符拼接,只会得到乱码(你看到的"字节码"),根本无法提取表格数据。
解决方案
针对.xlsx格式(推荐限制用户上传该格式,因为.xls的BIFF格式手动解析复杂度极高),我们可以利用其本质是ZIP压缩包的特性,手动解压后读取内部XML文件提取表格数据,再转换为CSV:
修改后的完整代码
using System.IO.Compression; using System.Xml.Linq; public void document_excel_OnFileUploaded_Staff(object sender, FileUploadedEventArgs e) { fu_documents_Staff.TargetFolder = "~/UploadedFiles"; DataTable dt = obj_common.Get_File_Code("CustDoc"); if (dt.Rows.Count > 0 && dt.Rows[0][0].ToString() != "") { string files_name = dt.Rows[0][0].ToString() + e.File.GetNameWithoutExtension().ToString() + e.File.GetExtension(); string filePath = Path.Combine(Server.MapPath(fu_documents_Staff.TargetFolder), files_name); // Save the uploaded file e.File.SaveAs(filePath); string csvFilePath = string.Empty; try { // Convert Excel to CSV csvFilePath = ConvertExcelToCsv(filePath); // Read the data from CSV using (StreamReader reader = new StreamReader(csvFilePath)) { string line; while ((line = reader.ReadLine()) != null) { Console.WriteLine(line); string[] values = line.Split(','); // Process the data as needed foreach (string value in values) { Console.WriteLine(value); } } } } catch (Exception ex) { Console.WriteLine($"转换失败:{ex.Message}"); } finally { // Cleanup the CSV file if it exists if (!string.IsNullOrEmpty(csvFilePath) && File.Exists(csvFilePath)) { File.Delete(csvFilePath); } } hdn_doc_name_Staff.Value = e.File.GetNameWithoutExtension().ToString() + e.File.GetExtension(); hdn_doc_sav_Staff.Value = files_name; lab_doc_name_out_Staff.Text = hdn_doc_name_Staff.Value; } } private string ConvertExcelToCsv(string excelFilePath) { string csvFilePath = Path.ChangeExtension(excelFilePath, ".csv"); string extension = Path.GetExtension(excelFilePath).ToLower(); if (extension == ".xlsx") { // .xlsx是ZIP压缩包,读取内部XML提取数据 using (ZipArchive zipArchive = ZipFile.OpenRead(excelFilePath)) { // 获取第一个工作表的XML文件(默认sheet1.xml) ZipArchiveEntry sheetEntry = zipArchive.GetEntry("xl/worksheets/sheet1.xml"); if (sheetEntry == null) { throw new InvalidOperationException("未找到工作表文件"); } using (StreamReader sheetReader = new StreamReader(sheetEntry.Open())) { XDocument sheetDoc = XDocument.Parse(sheetReader.ReadToEnd()); XNamespace ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; using (StreamWriter csvWriter = new StreamWriter(csvFilePath)) { // 遍历所有行 foreach (XElement row in sheetDoc.Descendants(ns + "row")) { StringBuilder csvLine = new StringBuilder(); bool isFirstCell = true; // 遍历行内所有单元格 foreach (XElement cell in row.Descendants(ns + "c")) { if (!isFirstCell) { csvLine.Append(','); } isFirstCell = false; string cellValue = cell.Element(ns + "v")?.Value ?? string.Empty; // 处理共享字符串(单元格类型为"s"时引用共享字符串表) if (cell.Attribute("t")?.Value == "s") { ZipArchiveEntry sharedStringEntry = zipArchive.GetEntry("xl/sharedStrings.xml"); if (sharedStringEntry != null) { using (StreamReader ssReader = new StreamReader(sharedStringEntry.Open())) { XDocument ssDoc = XDocument.Parse(ssReader.ReadToEnd()); XElement stringItem = ssDoc.Descendants(ns + "si").ElementAt(int.Parse(cellValue)); cellValue = stringItem.Descendants(ns + "t").FirstOrDefault()?.Value ?? string.Empty; } } } // 处理包含逗号或引号的内容,用双引号包裹并转义内部引号 if (cellValue.Contains(',') || cellValue.Contains('"')) { csvLine.Append('"').Append(cellValue.Replace("\"", "\"\"")).Append('"'); } else { csvLine.Append(cellValue); } } csvWriter.WriteLine(csvLine.ToString()); } } } } } else if (extension == ".xls") { // .xls为BIFF二进制格式,手动解析复杂度极高,暂不支持 throw new NotSupportedException("暂不支持解析.xls格式文件,请上传.xlsx文件"); } else { throw new ArgumentException("上传的文件不是有效的Excel文件"); } return csvFilePath; }
关键说明
- 核心逻辑:
.xlsx本质是包含多个XML文件的ZIP包,我们通过ZipFile解压读取工作表XML和共享字符串XML,提取单元格数据并转换为CSV格式。 - 注意事项:
- 仅处理第一个工作表(
sheet1.xml),如需处理其他工作表,需修改查找工作表入口的逻辑。 - 处理了共享字符串(Excel会将重复字符串存入共享表,单元格仅存索引)。
- 对包含逗号或引号的单元格内容做了CSV格式兼容处理。
- 若必须支持
.xls格式,建议调整需求或寻找其他兼容方案(但纯手动解析几乎不现实)。
- 仅处理第一个工作表(
- 依赖:确保项目引用
System.IO.Compression.FileSystem(.NET Framework需手动添加引用,.NET Core/5+默认包含)。
内容的提问来源于stack exchange,提问作者Fresher Developer
相关产品推荐
相关产品推荐

