Skip to content

Commit 2750557

Browse files
feat: Get-OpenXML simplification ( Fixes #2 )
1 parent b11fadb commit 2750557

File tree

1 file changed

+37
-97
lines changed

1 file changed

+37
-97
lines changed

Commands/Get-OpenXML.ps1

Lines changed: 37 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -25,46 +25,9 @@ function Get-OpenXML
2525
)
2626

2727
begin {
28-
# First lets declare a little helper function to get the content of a part
29-
filter getPartContent {
30-
$part = $_
31-
$partStream = $part.GetStream()
32-
if (-not $partStream) { return }
33-
switch ($part.ContentType) {
34-
# If the content type looks like XML, read it as XML
35-
{ $part.ContentType -match '[\./\+]xml' } {
36-
$streamReader = [IO.StreamReader]::new($partStream)
37-
$streamReader.ReadToEnd() -as [xml]
38-
$streamReader.Close()
39-
}
40-
# If the part looks like JSON, read it as JSON
41-
{ $part.Uri -match '\.json$'} {
42-
$streamReader = [IO.StreamReader]::new($partStream)
43-
$jsonContent = $streamReader.ReadToEnd()
44-
$streamReader.Close()
45-
$jsonContent | ConvertFrom-Json
46-
}
47-
# Otherwise, read it as a memory stream and return the byte array
48-
default {
49-
$outputStream = [IO.MemoryStream]::new()
50-
$partStream.CopyTo($outputStream)
51-
$outputStream.Seek(0, 'Begin')
52-
$outputStream.ToArray()
53-
}
54-
}
55-
56-
$partStream.Close()
57-
$partStream.Dispose()
58-
}
59-
}
60-
61-
process {
62-
# Try to resolve the file path
63-
$resolvedPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePath)
64-
# If we could not resolve the path, exit
65-
if (-not $resolvedPath ) { return }
6628

67-
foreach ($filePath in $resolvedPath) {
29+
filter openXMLFromFile {
30+
$filePath = $_
6831
# Get the file info and read the file as a byte stream.
6932
$fileInfo = $FilePath -as [IO.FileInfo]
7033
# By reading the file with Get-Content -AsByteStream, we avoid locking the file
@@ -77,85 +40,62 @@ function Get-OpenXML
7740
# Create a memory stream from the byte array
7841
$memoryStream = [IO.MemoryStream]::new($packageBytes)
7942
# and open the package from the memory stream
80-
$filePackage = [IO.Packaging.Package]::Open($memoryStream, "Open", "Read")
43+
$filePackage = [IO.Packaging.Package]::Open($memoryStream, "Open", "ReadWrite")
8144
# If that did not work, return.
8245
if (-not $filePackage) { return }
8346

84-
# Get the package relationships.
85-
# (these are important for key corner cases in OpenXML files)
86-
$packageRelationships = $filePackage.GetRelationships()
87-
$packageContent = [Ordered]@{}
88-
$packageParts = @($filePackage.GetParts())
89-
90-
# Now we will read each part in the package, and store it in an `[Ordered]` dictionary
91-
# Since this _might_ take a while (if you used a lot of PowerPoint images) we want to show a progress bar.
92-
93-
# Prepare the progress bar
94-
$partCount = 0
95-
$partTotal = $packageParts.Length
96-
$partProgress = [Ordered]@{Id=Get-Random;Activity='Reading Parts'}
97-
98-
# Then read each part
99-
foreach ($part in $packageParts) {
100-
$partCount++
101-
# update the progress bar
102-
Write-Progress @partProgress -Status "Reading part $($part.Uri) ($partCount of $partTotal)" -PercentComplete (
103-
[math]::Round(($partCount * 100/ $partTotal))
104-
)
105-
# and store the part in the dictionary
106-
$packageContent["$($part.Uri)"] =
107-
[PSCustomObject]@{
108-
PSTypeName = 'OpenXML.Part'
109-
Uri = $part.Uri
110-
ContentType = $part.ContentType
111-
# (we'll use our helper function to get the content)
112-
Content = $part | getPartContent
113-
FilePath = "$resolvedPath"
114-
}
115-
}
116-
# Now that we've read all parts, we can close the package
117-
$filePackage.Close()
118-
# and the memory stream, too.
119-
$memoryStream.Close()
120-
121-
# and finally, complete the progress bar.
122-
Write-Progress @partProgress -Status "Completed reading $partCount parts" -Completed
123-
124-
# Now we can create the final object.
125-
$OpenXMLObject = [PSCustomObject]@{
126-
# It is a generic OpenXML file by default
127-
PSTypeName = 'OpenXML.File'
128-
# with a `.FilePath`, so we can re-read and update it.
129-
FilePath = $resolvedPath
130-
# all of the `.Parts` have been read.
131-
Parts = $packageContent
132-
# and the package relationships are included, too.
133-
Relationships = $packageRelationships
134-
}
135-
47+
$filePackage.pstypenames.insert(0,'OpenXML')
48+
$filePackage.pstypenames.insert(0,'OpenXML.File')
49+
$packageContent = $filePackage.Parts
50+
$openXMLObject = $filePackage |
51+
Add-Member NoteProperty FilePath $filePath -Force -PassThru |
52+
Add-Member NoteProperty MemoryStream $memoryStream -Force -PassThru
53+
13654
# Now we can get more specific about what type of OpenXML file this is.
13755
# By looking for certain key parts, we can determine if this is a PowerPoint, Excel, or Word file.
13856
# For example, if the package contains a part with `/ppt/` in the URI,
139-
if ($packageContent.Keys -match '/ppt/') {
57+
if ($filePackage.Parts.Keys -match '/ppt/') {
14058
# it is an `OpenXML.PowerPoint.File`
14159
$openXmlObject.pstypenames.insert(0, 'OpenXML.PowerPoint.File')
14260
}
14361

14462
# If the package contains a part with `/xl/` in the URI,
145-
if ($packageContent.Keys -match '/xl/') {
63+
if ($filePackage.Parts.Keys -match '/xl/') {
14664
# it is an `OpenXML.Excel.File`
14765
$openXmlObject.pstypenames.insert(0, 'OpenXML.Excel.File')
14866
}
14967

15068
# If the package contains a part with `/word/` in the URI, it is a Word file.
151-
if ($packageContent.Keys -match '/word/') {
69+
if ($filePackage.Parts.Keys -match '/word/') {
15270
# it is an `OpenXML.Word.File`
15371
$openXmlObject.pstypenames.insert(0, 'OpenXML.Word.File')
15472
}
155-
73+
74+
# If the package contains a part with `/Documents/` in the URI,
75+
if ($filePackage.Parts.Keys -match '/Documents/') {
76+
# it is an `OpenXML.XPS.File`
77+
$openXmlObject.pstypenames.insert(0, 'OpenXML.XPS.File')
78+
}
15679

15780
# Now we output our openXML object
15881
$OpenXMLObject
159-
}
82+
}
83+
}
84+
85+
process {
86+
if ($filePath) {
87+
# Try to resolve the file path
88+
$resolvedPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePath)
89+
# If we could not resolve the path, exit
90+
if (-not $resolvedPath ) { return }
91+
92+
$resolvedPath | openXMLFromFile
93+
} else {
94+
$memoryStream = [IO.MemoryStream]::new()
95+
$EmptyPackage = [io.packaging.package]::Open($memoryStream ,'Create')
96+
$EmptyPackage | Add-Member NoteProperty -Name MemoryStream -Value $memoryStream -Force
97+
$EmptyPackage.pstypenames.insert(0, 'OpenXML')
98+
$EmptyPackage
99+
}
160100
}
161101
}

0 commit comments

Comments
 (0)