【问题标题】:How can I read xlsx files into .net without Excel or 3rd party tools?如何在没有 Excel 或 3rd 方工具的情况下将 xlsx 文件读入 .net?
【发布时间】:2019-08-15 02:35:56
【问题描述】:

我需要通过网络读取 Excel xlsx 文件,而不使用第三方工具或安装 Office。我可以为这个项目用 Powershell 或 C# 编写代码,但我没有找到符合我要求的任何一种语言的解决方案。它需要是原生 .net。

有没有人举例说明如何做到这一点?

此解决方案不起作用 powershell excel access without installing Excel

因为我没有注册“Microsoft.Jet.OLEDB.4.0”并且显然没有 从搜索来看,只能在32位机上使用?

【问题讨论】:

  • 我认为您只能从 32 位代码运行它,这并不完全相同。 .net 代码默认运行在 32 位模式,但 powershell 不运行。
  • 但是使用 3rd 方工具有什么问题?大多数可以读取excel文件的.net文件都是免费使用的。 OledB 将允许您对工作表进行 SQL 查询,但尚不清楚这是否足以满足您的功能
  • 链接中提到的 EPPlus.dll 有什么问题。
  • xlsx 文件本质上只是带有 xml 文件的 zip 存档,其中包含来自各种工作表的数据,等等。如果您真的不希望使用任何第三方工具,您可以通过一些努力阅读 xml 并使用它。
  • EPPlus 是一个非常好的库,您可以在项目中使用它,它可以帮助您读取 excel 文件,而无需安装任何额外的第三方软件。

标签: c# excel powershell


【解决方案1】:

ImportExcel PowerShell 模块是EPPLus.dll 的包装器。它提供了简单的命令,您可以调用这些命令与 Excel 文件进行交互(仅限 xlxs)。

存储库位于此处,由 Doug Finke 编写:https://github.com/dfinke/ImportExcel

您也可以通过Install-Module ImportExcel从 PowerShell 库中获取它

【讨论】:

  • 我以前用过这个,效果很好,可惜我不能把它放在服务器上
【解决方案2】:

如果你决定使用 C#,你可以使用 OpenXML。只需使用 Nuget 包管理器安装它。它不需要安装 Office。

例子:

      using (SpreadsheetDocument spreadsheetDocument =   SpreadsheetDocument.Open("myFile.xlsx", false))
        {
            WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
            WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
            SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
            foreach (Row r in sheetData.Elements<Row>())
            {
                foreach (Cell c in r.Elements<Cell>())
                {
                    Console.Write(c.InnerText + ",");
                }
                Console.WriteLine("\n");
            }
            Console.WriteLine();
            Console.ReadKey();
        }

并将其添加到 using 的

using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

【讨论】:

    【解决方案3】:

    这不是针对所有场景的完整解决方案,因为我没有时间绘制每个 xml,但这应该让任何人开始遇到类似的情况。

    希望这将节省其他人开发类似东西的时间,下面的代码将 xlsx 转换为 zip 文件,解压缩,然后映射不同的 xml 以创建电子表格的虚拟表示。

    这不是最快的代码,但如果您正在寻找速度,那么无论如何您都不会使用 Powershell。

    <#
    
        $columnListOfTuples and $rowListOfTuples have everything mapped
    
        Each xlsx file is a compressed group of xml files into a zip file that is renamed .xlsx
        There is an xml of shared strings that are then represented by a single numerical value to save space
        There is an xml for each sheet in the spread sheet that has numerical references to the values in the shared strings xml
        To produce a virtual/programmatic representation of the spreadsheet, the code will read the sheet, then read the numerical reference than then cross reference that to the shared strings value
        I have no mapped out all functions and xmls in the compressed file, but this is enough to get you started
        It's only configured for columns A - Z, you'll have to write code for additional columsn
        You need powershell 5.0 or later to have the native unzip feature
        It assumes your first worksheet is named worksheet1
    #>
    
    cls;
    
    #Region Read Xlsx file
    
        #Region Create temporary zip file and location
    
        $fileDate = [DateTime]::Now.ToShortDateString();
        $fileDate = $fileDate.Replace("/", "-");
        $fileTime = [DateTime]::Now.ToShortTimeString();
        $fileTime = $fileTime.Replace(":", ".");
        $fileTime = $fileTime.Replace(" ", ".");
        $fileDateTime = $fileDate + "_" + $fileTime;
    
        $networkFile = "http://[xxxxx].xlsx";
        $originalFile = "c:\temp\file.xlsx"; # file must exist and location must have writable rights
        #Invoke-WebRequest -Uri $networkFile -OutFile $originalFile -UseDefaultCredentials; # this is to download a network file locally, may not be needed
    
        $index = $originalFile.LastIndexOf("\");
        $originalFileDirectory = $originalFile.Remove($index);
        $tempCopiedFileName = $originalFileDirectory + "\tempXlsx-" + $fileDateTime + ".zip";
        $desinationPath = $originalFileDirectory + "\xlsxDecompressed";
    
        #Endregion /Create temporary zip file and location
    
        #Region Map all of the zip/xlsx file paths
    
        $relsXmlFile = $desinationPath + "\_rels\.rels";
        $item1XmlRelsXmlFile = $desinationPath + "\customXml\_rels\item1.xml.rels";
        $item2XmlRelsXmlFile = $desinationPath + "\customXml\_rels\item2.xml.rels";
        $item3XmlRelsXmlFile = $desinationPath + "\customXml\_rels\item3.xml.rels";
        $item1XmlFile = $desinationPath + "\customXml\item1.xml";
        $item2XmlFile = $desinationPath + "\customXml\item2.xml";
        $item3XmlFile = $desinationPath + "\customXml\item3.xml";
        $itemProps1XmlFile = $desinationPath + "\customXml\itemProps1.xml";
        $itemProps2XmlFile = $desinationPath + "\customXml\itemProps2.xml";
        $itemProps3XmlFile = $desinationPath + "\customXml\itemProps3.xml";
        $appXmlFile = $desinationPath + "\[filename]\docProps\app.xml";
        $coreXmlFile = $desinationPath + "\[filename]\docProps\core.xml";
        $customXmlFile = $desinationPath + "\[filename]\docProps\custom.xml";
        $workbookXmlRelsXmlFile = $desinationPath + "\xl\_rels\workbook.xml.rels";
        $theme1XmlFile = $desinationPath + "\xl\theme\theme1.xml";
        $sheet1XmlRelsXmlFile = $desinationPath + "\xl\worksheets|_rels|sheet1.xml.rels";
        $workSheet1XmlFile = $desinationPath + "\xl\worksheets\sheet1.xml";
        $sharedStringsXmlFile = $desinationPath + "\xl\sharedStrings.xml";
        $stylesXmlFile = $desinationPath + "\xl\styles.xml";
        $workbookXmlFile = $desinationPath + "\xl\workbook.xml";
        $contentTypesXmlFile = $desinationPath + "\[Content_Types].xml";
    
        #Endregion /Map all of the zip/xlsx file paths
    
        Copy-Item $originalFile -Destination $tempCopiedFileName -Recurse; # copy the xlsx file as a temporary zip file
        Expand-Archive -LiteralPath $tempCopiedFileName -DestinationPath $desinationPath -Force; # unzip the file
        [xml]$fileLines = Get-Content($sharedStringsXmlFile); # read the contents of the shared strings file
    
        #Region Map shared strings
    
        $arr_SharedStrings = New-Object System.Collections.Generic.List[String];
        for ($i = 0; $i -lt $fileLines.sst.si.Count; $i++ )
        {
            $line = $fileLines.sst.si[$i];
            $tuple = [tuple]::create([int]$i, [string]("`"" + $line.InnerText + "`"") );
            $arr_SharedStrings.Add($tuple); # map the shared strings to the representational number inside of the xml files
        }
    
        #Endregion /Map shared strings
    
        [xml]$fileLines = Get-Content($workSheet1XmlFile);
        $sheetData = $fileLines.worksheet.sheetData;
    
        #Region Map rows
        $arr_sheet1_rows = New-Object System.Collections.ArrayList; # this will have a list/tuple of each cell that makes up a row
        for ($i = 0; $i -lt $sheetData.row.Count; $i++ )
        {
            $arr_sheet1_individualRow = New-Object System.Collections.Generic.List[String];
            $rowElements = $sheetData.row[$i].c; #grab data of all cells in that row
            foreach ($element in $rowElements)
            {
                $elementsTuple = [tuple]::create([string]$element.r, [string]$element.s, [string]$element.v)
                $arr_sheet1_individualRow.Add($elementsTuple);      
            }
    
            $rowTuple = [Tuple]::create([int] $i+1, $arr_sheet1_individualRow);
            $arr_sheet1_rows.Add($rowTuple) | Out-Null;
        }
    
        $rowListOfTuples = New-Object System.Collections.ArrayList; # has row number, then column letter, then cell value
        foreach ($rowMapped in $arr_sheet1_rows)
        {
            $cellValues = $rowMapped.Item2;
            $rowNumber = $rowMapped.Item1;
            foreach ($cellValue in $cellValues)
            {
                $cellValue = $cellValue.Remove(0, 1);
                $cellValue = $cellValue.Remove($cellValue.Length - 1, 1);
                $cellValue = $cellValue.Replace(" ", "");
                $cellValuesSplit = $cellValue.Split(",");
                $columnLetter = $cellValuesSplit[0];
                $columnLetter = $columnLetter -replace '[0-9]',''
                $cellValueOnly = $cellValuesSplit[2];
    
                $cellTuple = [tuple]::create([int]$rowNumber, [string]$columnLetter, [string]$cellValueOnly);
                $rowListOfTuples.Add($cellTuple) | Out-Null;
            }
        }
    
        #Endregion /Map shared rows
    
        #Region Map shared columns
    
        $rowCount = $arr_sheet1_rows.Count;
        $sheetDataCols = $fileLines.worksheet.cols;
        $arr_sheet1_columns = New-Object System.Collections.ArrayList; # this will have a list/tuple of each cell that makes up a column
        #$arr_sheet1_columns.Add("Place holder") | Out-Null;
        #for ($i = 0; $i -lt $sheetDataCols.col.Count; $i++ )
        for ($i = 0; $i -lt $rowCount; $i++ )
        {
            if ($arr_sheet1_columns.Count -lt $sheetDataCols.col.Count)
            {
                for ($j = 0; $j -lt $sheetDataCols.col.Count; $j++ )
                {
                    switch  ($j)
                    {
                        0 { $columnLetterTranslated = "A"; break; }
                        1 { $columnLetterTranslated = "B"; break; }
                        2 { $columnLetterTranslated = "C"; break; }
                        3 { $columnLetterTranslated = "D"; break; }
                        4 { $columnLetterTranslated = "E"; break; }
                        5 { $columnLetterTranslated = "F"; break; }
                        6 { $columnLetterTranslated = "G"; break; }
                        7 { $columnLetterTranslated = "H"; break; }
                        8 { $columnLetterTranslated = "I"; break; }
                        9 { $columnLetterTranslated = "J"; break; }
                        10 { $columnLetterTranslated = "K"; break; }
                        11 { $columnLetterTranslated = "L"; break; }
                        12 { $columnLetterTranslated = "M"; break; }
                        13 { $columnLetterTranslated = "N"; break; }
                        14 { $columnLetterTranslated = "O"; break; }
                        15 { $columnLetterTranslated = "P"; break; }
                        16 { $columnLetterTranslated = "Q"; break; }
                        17 { $columnLetterTranslated = "R"; break; }
                        18 { $columnLetterTranslated = "S"; break; }
                        19 { $columnLetterTranslated = "T"; break; }
                        20 { $columnLetterTranslated = "U"; break; }
                        21 { $columnLetterTranslated = "V"; break; }
                        22 { $columnLetterTranslated = "W"; break; }
                        23 { $columnLetterTranslated = "X"; break; }
                        24 { $columnLetterTranslated = "Y"; break; }
                        25 { $columnLetterTranslated = "Z"; break; }
                    }
                    $arr_sheet1_columns.Add($columnLetterTranslated) | Out-Null;
                }       
            }
    
            $rowElements = $sheetData.row[$i].c; #grab data of all cells in that row
            foreach ($element in $rowElements)
            {
                $columnLetter = $element.r -replace '[^a-zA-Z-]',''
                for ($k = 0; $k -lt $arr_sheet1_columns.Count; $k++)
                {
                    $column = $arr_sheet1_columns[$k];
                    if ($column.StartsWith($columnLetter))
                    {
                        $columnTemp = $column.ToString() + "|" + $element.v;
                        $arr_sheet1_columns.Remove($column);
                        $arr_sheet1_columns.Insert($k, $columnTemp);
                        $stop = "here";
                    }
                }
            }
        }
    
        $columnListOfTuples = New-Object System.Collections.ArrayList; # has column letter, than row number, then cell value
        foreach ($columnMapped in $arr_sheet1_columns)
        {
            $index = $columnMapped.IndexOf("|");
            $columnMappedLetter = $columnMapped.Remove($index);
    
            $columnIndividualValues = $columnMapped.Remove(0, $index);
            $columnIndividualValuesSplit = $columnIndividualValues.Split("|");
    
            $rowNumber = 0;
            foreach($columnIndividualValueSplit in $columnIndividualValuesSplit)
            {   
                $cellTuple = [tuple]::create([string]$columnMappedLetter, [int]$rowNumber, [string]$columnIndividualValueSplit);
                $columnListOfTuples.Add($cellTuple) | Out-Null;
                $rowNumber ++;
            }
        }
    
        #Endregion /Map shared columns
    
    
    
        # Here you have all of the data and you can parse it however you want, below is an example of that
    
        #Region Parse the extracted data
    
        $rowsForParsing = New-Object System.Collections.ArrayList;
        foreach ($arr_sheet1_row in $arr_sheet1_rows)
        {
            $rowNumber = $arr_sheet1_row.Item1;
            $cellValues = New-Object System.Collections.ArrayList; 
            $rowTemp = "";
    
            $indexer = 0;
            foreach ($rowValue in $arr_sheet1_row.Item2)
            {
                $valueSplit = $rowValue.Replace(" ", "");
                $valueSplit = $valueSplit.Replace("(", "");
                $valueSplit = $valueSplit.Replace(")", "");
                $valueSplit = $valueSplit.Split(",");
    
                $cellLocation = "";
                $cellLocation = $valueSplit[0];
                $cellSType = "";
                $cellSType = $valueSplit[1];
                $cellStringRepresentationValue = "";
                $cellStringRepresentationValue = $valueSplit[2];
                $translatedValue = "";
    
                if ($cellStringRepresentationValue -ne "")
                {
                    switch ($cellSType)
                    {
                        "1" { $translatedValue = getStringMappedValue($cellStringRepresentationValue); break;}  # represents column headers
                        "2" { $translatedValue = [datetime]::FromOADate($cellStringRepresentationValue); break;}  # number of days from January 1, 1900
                        "3" { $translatedValue = getStringMappedValue($cellStringRepresentationValue); break;}  # mapped value to strings table
                        "4" {$translatedValue = getStringMappedValue($cellStringRepresentationValue); break;}  # mapped value to strings table
                        "5" { $translatedValue = getStringMappedValue($cellStringRepresentationValue); break;}  # mapped value to strings table
                        "6" { Write-Host "#6: " $rowValue; break;}  # not sure what 6 represents yet
                        "7" { Write-Host "#7: " $rowValue; break;}  # not sure what 7 represents yet
                        "8" { Write-Host "#8: " $rowValue; break;}  # not sure what 8 represents yet
                        "9" { Write-Host "#9: " $rowValue; break;}  # not sure what 9 represents yet
                        "10" { Write-Host "#10: " $rowValue; break;}  # not sure what 10 represents yet
                        "11" { Write-Host "#11: " $rowValue; break;}  # not sure what 11 represents yet     
                    }
                }
    
                if (($rowTemp -eq "") -and ($indexer -eq 0))
                {
                    $rowTemp = "$translatedValue";
                }
                else
                {       
                    $rowTemp = "$rowTemp" + "|" + "$translatedValue";
                }   
    
                $indexer++;
            }
    
            if ($rowNumber -eq 22)
            {
                $stop = "here";
            }
    
            $rowsForParsing.Add("$rowNumber|$rowTemp") | Out-Null;
        }
    
        $fileInformation = New-Object System.Collections.ArrayList;
        $topRow = $rowsForParsing[1];
        foreach ($rowParsed in $rowsForParsing)
        {
            $rowParsedSplit = $rowParsed.Split("|");    
            $date = $rowParsedSplit[1];
    
            $pass = $true;
            try
            {
                $dtVal = get-date $date;
            }
            catch
            {
                $pass = $false;
            }
    
            if ($pass -eq $true)
            {
                if ((get-date) -gt (get-date $date))
                {
                    $dateApplicableRow = $rowParsed;
                }
                else
                {
                    Write-Host "Top row is $topRow";
                    Write-Host "This weeks row is $dateApplicableRow";
                    $fileInformation.Add($topRow);
                    $fileInformation.Add($dateApplicableRow);
                    break;
                }
            }
        }
    
        #Endregion /Parse the extracted data
    
    #EndRegion /Read Xlsx file
    

    【讨论】:

      猜你喜欢
      • 2015-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-21
      • 1970-01-01
      • 1970-01-01
      • 2015-03-26
      相关资源
      最近更新 更多