mirror of
https://github.com/dfinke/ImportExcel.git
synced 2025-12-15 07:43:23 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abdd37b09e | ||
|
|
e1e855a823 | ||
|
|
585d9686a6 | ||
|
|
e87a6bdaf5 | ||
|
|
5ded5111b4 | ||
|
|
914c61048b | ||
|
|
25fb76d9b7 | ||
|
|
7faa27a3b3 | ||
|
|
5700be0684 | ||
|
|
6d86108060 | ||
|
|
4581c2b3e9 | ||
|
|
142c31ccc1 |
@@ -4,7 +4,7 @@
|
|||||||
RootModule = 'ImportExcel.psm1'
|
RootModule = 'ImportExcel.psm1'
|
||||||
|
|
||||||
# Version number of this module.
|
# Version number of this module.
|
||||||
ModuleVersion = '6.2.1'
|
ModuleVersion = '6.2.2'
|
||||||
|
|
||||||
# ID used to uniquely identify this module
|
# ID used to uniquely identify this module
|
||||||
GUID = '60dd4136-feff-401a-ba27-a84458c57ede'
|
GUID = '60dd4136-feff-401a-ba27-a84458c57ede'
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ if ($PSVersionTable.PSVersion.Major -ge 5) {
|
|||||||
. $PSScriptRoot\plot.ps1
|
. $PSScriptRoot\plot.ps1
|
||||||
|
|
||||||
Function New-Plot {
|
Function New-Plot {
|
||||||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification='New-Plot does not change system state')]
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'New-Plot does not change system state')]
|
||||||
Param()
|
Param()
|
||||||
|
|
||||||
[PSPlot]::new()
|
[PSPlot]::new()
|
||||||
@@ -269,7 +269,6 @@ function Import-Excel {
|
|||||||
[Parameter(ParameterSetName = "PathA", Mandatory, ValueFromPipelineByPropertyName, ValueFromPipeline, Position = 0 )]
|
[Parameter(ParameterSetName = "PathA", Mandatory, ValueFromPipelineByPropertyName, ValueFromPipeline, Position = 0 )]
|
||||||
[Parameter(ParameterSetName = "PathB", Mandatory, ValueFromPipelineByPropertyName, ValueFromPipeline, Position = 0 )]
|
[Parameter(ParameterSetName = "PathB", Mandatory, ValueFromPipelineByPropertyName, ValueFromPipeline, Position = 0 )]
|
||||||
[Parameter(ParameterSetName = "PathC", Mandatory, ValueFromPipelineByPropertyName, ValueFromPipeline, Position = 0 )]
|
[Parameter(ParameterSetName = "PathC", Mandatory, ValueFromPipelineByPropertyName, ValueFromPipeline, Position = 0 )]
|
||||||
[ValidateScript( {(Test-Path -Path $_ -PathType Leaf) -and ($_ -match '.xls$|.xlsx$|.xlsm$')})]
|
|
||||||
[String]$Path,
|
[String]$Path,
|
||||||
[Parameter(ParameterSetName = "PackageA", Mandatory)]
|
[Parameter(ParameterSetName = "PackageA", Mandatory)]
|
||||||
[Parameter(ParameterSetName = "PackageB", Mandatory)]
|
[Parameter(ParameterSetName = "PackageB", Mandatory)]
|
||||||
@@ -300,12 +299,13 @@ function Import-Excel {
|
|||||||
)
|
)
|
||||||
begin {
|
begin {
|
||||||
$sw = [System.Diagnostics.Stopwatch]::StartNew()
|
$sw = [System.Diagnostics.Stopwatch]::StartNew()
|
||||||
|
|
||||||
Function Get-PropertyNames {
|
Function Get-PropertyNames {
|
||||||
<#
|
<#
|
||||||
.SYNOPSIS
|
.SYNOPSIS
|
||||||
Create objects containing the column number and the column name for each of the different header types.
|
Create objects containing the column number and the column name for each of the different header types.
|
||||||
#>
|
#>
|
||||||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification="Name would be incorrect, and command is not exported")]
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "Name would be incorrect, and command is not exported")]
|
||||||
Param (
|
Param (
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory)]
|
||||||
[Int[]]$Columns,
|
[Int[]]$Columns,
|
||||||
@@ -318,13 +318,13 @@ function Import-Excel {
|
|||||||
$i = 0
|
$i = 0
|
||||||
foreach ($C in $Columns) {
|
foreach ($C in $Columns) {
|
||||||
$i++
|
$i++
|
||||||
$C | Select-Object @{N = 'Column'; E = {$_}}, @{N = 'Value'; E = {'P' + $i}}
|
$C | Select-Object @{N = 'Column'; E = { $_ } }, @{N = 'Value'; E = { 'P' + $i } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif ($HeaderName) {
|
elseif ($HeaderName) {
|
||||||
$i = 0
|
$i = 0
|
||||||
foreach ($H in $HeaderName) {
|
foreach ($H in $HeaderName) {
|
||||||
$H | Select-Object @{N = 'Column'; E = {$Columns[$i]}}, @{N = 'Value'; E = {$H}}
|
$H | Select-Object @{N = 'Column'; E = { $Columns[$i] } }, @{N = 'Value'; E = { $H } }
|
||||||
$i++
|
$i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -334,7 +334,7 @@ function Import-Excel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($C in $Columns) {
|
foreach ($C in $Columns) {
|
||||||
$Worksheet.Cells[$StartRow, $C] | Where-Object {$_.Value} | Select-Object @{N = 'Column'; E = {$C}}, Value
|
$Worksheet.Cells[$StartRow, $C] | Where-Object { $_.Value } | Select-Object @{N = 'Column'; E = { $C } }, Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -346,43 +346,56 @@ function Import-Excel {
|
|||||||
|
|
||||||
process {
|
process {
|
||||||
if ($path) {
|
if ($path) {
|
||||||
|
$extension = [System.IO.Path]::GetExtension($Path)
|
||||||
|
if ($extension -notmatch '.xlsx$|.xlsm$') {
|
||||||
|
throw "Import-Excel does not support reading this extension type $($extension)"
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolvedPath = (Resolve-Path $Path -ErrorAction SilentlyContinue)
|
||||||
|
if ($resolvedPath) {
|
||||||
|
$Path = $resolvedPath.ProviderPath
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw "'$($Path)' file not found"
|
||||||
|
}
|
||||||
|
|
||||||
$stream = New-Object -TypeName System.IO.FileStream -ArgumentList $Path, 'Open', 'Read', 'ReadWrite'
|
$stream = New-Object -TypeName System.IO.FileStream -ArgumentList $Path, 'Open', 'Read', 'ReadWrite'
|
||||||
if ($Password) {$ExcelPackage = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $stream , $Password }
|
if ($Password) { $ExcelPackage = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $stream , $Password }
|
||||||
else {$ExcelPackage = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $stream}
|
else { $ExcelPackage = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $stream }
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
#Select worksheet
|
#Select worksheet
|
||||||
if (-not $WorksheetName) { $Worksheet = $ExcelPackage.Workbook.Worksheets[1] }
|
if (-not $WorksheetName) { $Worksheet = $ExcelPackage.Workbook.Worksheets[1] }
|
||||||
elseif(-not ($Worksheet = $ExcelPackage.Workbook.Worksheets[$WorkSheetName])) {
|
elseif (-not ($Worksheet = $ExcelPackage.Workbook.Worksheets[$WorkSheetName])) {
|
||||||
throw "Worksheet '$WorksheetName' not found, the workbook only contains the worksheets '$($ExcelPackage.Workbook.Worksheets)'. If you only wish to select the first worksheet, please remove the '-WorksheetName' parameter." ; return
|
throw "Worksheet '$WorksheetName' not found, the workbook only contains the worksheets '$($ExcelPackage.Workbook.Worksheets)'. If you only wish to select the first worksheet, please remove the '-WorksheetName' parameter." ; return
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Debug $sw.Elapsed.TotalMilliseconds
|
Write-Debug $sw.Elapsed.TotalMilliseconds
|
||||||
#region Get rows and columns
|
#region Get rows and columns
|
||||||
#If we are doing dataonly it is quicker to work out which rows to ignore before processing the cells.
|
#If we are doing dataonly it is quicker to work out which rows to ignore before processing the cells.
|
||||||
if (-not $EndRow ) {$EndRow = $Worksheet.Dimension.End.Row }
|
if (-not $EndRow ) { $EndRow = $Worksheet.Dimension.End.Row }
|
||||||
if (-not $EndColumn) {$EndColumn = $Worksheet.Dimension.End.Column }
|
if (-not $EndColumn) { $EndColumn = $Worksheet.Dimension.End.Column }
|
||||||
$endAddress = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$EndRow]C[$EndColumn]", 0, 0)
|
$endAddress = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$EndRow]C[$EndColumn]", 0, 0)
|
||||||
if ($DataOnly) {
|
if ($DataOnly) {
|
||||||
#If we are using headers startrow will be the header-row so examine data from startRow + 1,
|
#If we are using headers startrow will be the header-row so examine data from startRow + 1,
|
||||||
if ($NoHeader) {$range = "A" + ($StartRow ) + ":" + $endAddress }
|
if ($NoHeader) { $range = "A" + ($StartRow ) + ":" + $endAddress }
|
||||||
else {$range = "A" + ($StartRow + 1 ) + ":" + $endAddress }
|
else { $range = "A" + ($StartRow + 1 ) + ":" + $endAddress }
|
||||||
#We're going to look at every cell and build 2 hash tables holding rows & columns which contain data.
|
#We're going to look at every cell and build 2 hash tables holding rows & columns which contain data.
|
||||||
#Want to Avoid 'select unique' operations & large Sorts, becuse time time taken increases with square
|
#Want to Avoid 'select unique' operations & large Sorts, becuse time time taken increases with square
|
||||||
#of number of items (PS uses heapsort at large size). Instead keep a list of what we have seen,
|
#of number of items (PS uses heapsort at large size). Instead keep a list of what we have seen,
|
||||||
#using Hash tables: "we've seen it" is all we need, no need to worry about "seen it before" / "Seen it many times".
|
#using Hash tables: "we've seen it" is all we need, no need to worry about "seen it before" / "Seen it many times".
|
||||||
$colHash = @{}
|
$colHash = @{ }
|
||||||
$rowHash = @{}
|
$rowHash = @{ }
|
||||||
foreach ($cell in $Worksheet.Cells[$range]) {
|
foreach ($cell in $Worksheet.Cells[$range]) {
|
||||||
if ($null -ne $cell.Value ) {$colHash[$cell.Start.Column] = 1; $rowHash[$cell.Start.row] = 1 }
|
if ($null -ne $cell.Value ) { $colHash[$cell.Start.Column] = 1; $rowHash[$cell.Start.row] = 1 }
|
||||||
}
|
}
|
||||||
$rows = ( $StartRow..$EndRow ).Where( {$rowHash[$_]})
|
$rows = ( $StartRow..$EndRow ).Where( { $rowHash[$_] })
|
||||||
$columns = ($StartColumn..$EndColumn).Where( {$colHash[$_]})
|
$columns = ($StartColumn..$EndColumn).Where( { $colHash[$_] })
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$Columns = $StartColumn .. $EndColumn ; if ($StartColumn -gt $EndColumn) {Write-Warning -Message "Selecting columns $StartColumn to $EndColumn might give odd results."}
|
$Columns = $StartColumn .. $EndColumn ; if ($StartColumn -gt $EndColumn) { Write-Warning -Message "Selecting columns $StartColumn to $EndColumn might give odd results." }
|
||||||
if ($NoHeader) {$Rows = $StartRow..$EndRow ; if ($StartRow -gt $EndRow) {Write-Warning -Message "Selecting rows $StartRow to $EndRow might give odd results."} }
|
if ($NoHeader) { $Rows = $StartRow..$EndRow ; if ($StartRow -gt $EndRow) { Write-Warning -Message "Selecting rows $StartRow to $EndRow might give odd results." } }
|
||||||
else {$Rows = (1 + $StartRow)..$EndRow ; if ($StartRow -ge $EndRow) {Write-Warning -Message "Selecting $StartRow as the header with data in $(1+$StartRow) to $EndRow might give odd results."}}
|
else { $Rows = (1 + $StartRow)..$EndRow } # ; if ($StartRow -ge $EndRow) { Write-Warning -Message "Selecting $StartRow as the header with data in $(1+$StartRow) to $EndRow might give odd results." } }
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
#region Create property names
|
#region Create property names
|
||||||
@@ -402,7 +415,7 @@ function Import-Excel {
|
|||||||
foreach ($R in $Rows) {
|
foreach ($R in $Rows) {
|
||||||
#Disabled write-verbose for speed
|
#Disabled write-verbose for speed
|
||||||
# Write-Verbose "Import row '$R'"
|
# Write-Verbose "Import row '$R'"
|
||||||
$NewRow = [Ordered]@{}
|
$NewRow = [Ordered]@{ }
|
||||||
|
|
||||||
foreach ($P in $PropertyNames) {
|
foreach ($P in $PropertyNames) {
|
||||||
$NewRow[$P.Value] = $Worksheet.Cells[$R, $P.Column].Value
|
$NewRow[$P.Value] = $Worksheet.Cells[$R, $P.Column].Value
|
||||||
@@ -417,7 +430,7 @@ function Import-Excel {
|
|||||||
}
|
}
|
||||||
catch { throw "Failed importing the Excel workbook '$Path' with worksheet '$Worksheetname': $_"; return }
|
catch { throw "Failed importing the Excel workbook '$Path' with worksheet '$Worksheetname': $_"; return }
|
||||||
finally {
|
finally {
|
||||||
if ($Path) {$stream.close(); $ExcelPackage.Dispose() }
|
if ($Path) { $stream.close(); $ExcelPackage.Dispose() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -463,9 +476,9 @@ function ConvertFrom-ExcelSheet {
|
|||||||
$xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Stream
|
$xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Stream
|
||||||
$workbook = $xl.Workbook
|
$workbook = $xl.Workbook
|
||||||
|
|
||||||
$targetSheets = $workbook.Worksheets | Where-Object {$_.Name -like $SheetName}
|
$targetSheets = $workbook.Worksheets | Where-Object { $_.Name -like $SheetName }
|
||||||
|
|
||||||
$params = @{} + $PSBoundParameters
|
$params = @{ } + $PSBoundParameters
|
||||||
$params.Remove("OutputPath")
|
$params.Remove("OutputPath")
|
||||||
$params.Remove("SheetName")
|
$params.Remove("SheetName")
|
||||||
$params.Remove('Extension')
|
$params.Remove('Extension')
|
||||||
@@ -496,7 +509,7 @@ function Export-MultipleExcelSheets {
|
|||||||
[Switch]$AutoSize
|
[Switch]$AutoSize
|
||||||
)
|
)
|
||||||
|
|
||||||
$parameters = @{} + $PSBoundParameters
|
$parameters = @{ } + $PSBoundParameters
|
||||||
$parameters.Remove("InfoMap")
|
$parameters.Remove("InfoMap")
|
||||||
$parameters.Remove("Show")
|
$parameters.Remove("Show")
|
||||||
|
|
||||||
@@ -509,7 +522,7 @@ function Export-MultipleExcelSheets {
|
|||||||
& $entry.Value | Export-Excel @parameters
|
& $entry.Value | Export-Excel @parameters
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($Show) {Invoke-Item $Path}
|
if ($Show) { Invoke-Item $Path }
|
||||||
}
|
}
|
||||||
|
|
||||||
Function WorksheetArgumentCompleter {
|
Function WorksheetArgumentCompleter {
|
||||||
@@ -519,7 +532,7 @@ Function WorksheetArgumentCompleter {
|
|||||||
$xlpkg = Open-ExcelPackage -ReadOnly -Path $xlPath
|
$xlpkg = Open-ExcelPackage -ReadOnly -Path $xlPath
|
||||||
$WorksheetNames = $xlPkg.Workbook.Worksheets.Name
|
$WorksheetNames = $xlPkg.Workbook.Worksheets.Name
|
||||||
Close-ExcelPackage -nosave -ExcelPackage $xlpkg
|
Close-ExcelPackage -nosave -ExcelPackage $xlpkg
|
||||||
$WorksheetNames.where( {$_ -like "*$wordToComplete*"}) | foreach-object {
|
$WorksheetNames.where( { $_ -like "*$wordToComplete*" }) | foreach-object {
|
||||||
New-Object -TypeName System.Management.Automation.CompletionResult -ArgumentList "'$_'",
|
New-Object -TypeName System.Management.Automation.CompletionResult -ArgumentList "'$_'",
|
||||||
$_ , ([System.Management.Automation.CompletionResultType]::ParameterValue) , $_
|
$_ , ([System.Management.Automation.CompletionResultType]::ParameterValue) , $_
|
||||||
}
|
}
|
||||||
|
|||||||
11
README.md
11
README.md
@@ -53,6 +53,17 @@ Install-Module ImportExcel -scope CurrentUser
|
|||||||
Install-Module ImportExcel
|
Install-Module ImportExcel
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# What's new 6.2.1
|
||||||
|
|
||||||
|
- Added requested feature, chart trendlines.
|
||||||
|
- [Example PowerShell script](https://github.com/dfinke/ImportExcel/blob/master/Examples/Charts/NumberOfVisitors.ps1)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# What's new 6.2.2
|
||||||
|
|
||||||
|
- Fixed Import-Excel and relative path issue, added unit tests.
|
||||||
|
|
||||||
# What's new 6.2.0
|
# What's new 6.2.0
|
||||||
Thank you to [James O'Neill](https://github.com/jhoneill)
|
Thank you to [James O'Neill](https://github.com/jhoneill)
|
||||||
|
|
||||||
|
|||||||
38
__tests__/Path.tests.ps1
Normal file
38
__tests__/Path.tests.ps1
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
Describe "Test reading relative paths" {
|
||||||
|
BeforeAll {
|
||||||
|
$script:xlfileName = "TestR.xlsx"
|
||||||
|
@{data = 1 } | Export-Excel "$pwd\TestR.xlsx"
|
||||||
|
}
|
||||||
|
|
||||||
|
AfterAll {
|
||||||
|
Remove-Item "$pwd\$($script:xlfileName)"
|
||||||
|
}
|
||||||
|
|
||||||
|
It "Should read local file" {
|
||||||
|
$actual = Import-Excel -Path ".\$($script:xlfileName)"
|
||||||
|
$actual | Should Not Be $null
|
||||||
|
$actual.Count | Should Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It "Should read with pwd" {
|
||||||
|
$actual = Import-Excel -Path "$pwd\$($script:xlfileName)"
|
||||||
|
$actual | Should Not Be $null
|
||||||
|
}
|
||||||
|
|
||||||
|
It "Should read with just a file name and resolve to cwd" {
|
||||||
|
$actual = Import-Excel -Path "$($script:xlfileName)"
|
||||||
|
$actual | Should Not Be $null
|
||||||
|
}
|
||||||
|
|
||||||
|
It "Should fail for not found" {
|
||||||
|
{ Import-Excel -Path "ExcelFileDoesNotExist.xlsx" } | Should Throw "'ExcelFileDoesNotExist.xlsx' file not found"
|
||||||
|
}
|
||||||
|
|
||||||
|
It "Should fail for xls extension" {
|
||||||
|
{ Import-Excel -Path "ExcelFileDoesNotExist.xls" } | Should Throw "Import-Excel does not support reading this extension type .xls"
|
||||||
|
}
|
||||||
|
|
||||||
|
It "Should fail for xlsxs extension" {
|
||||||
|
{ Import-Excel -Path "ExcelFileDoesNotExist.xlsxs" } | Should Throw "Import-Excel does not support reading this extension type .xlsxs"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
__tests__/testRelative.xlsx
Normal file
BIN
__tests__/testRelative.xlsx
Normal file
Binary file not shown.
BIN
images/ChartTrendlines.png
Normal file
BIN
images/ChartTrendlines.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
Reference in New Issue
Block a user