tech.guitarrapc.cóm

Technical updates

PowerShellでOpen XML形式のMS Officeに保存された画像ファイルを抽出する

PowerShellを勉強する中、標準シェル環境でZIP処理ができると今更気付きました…… ZIPの解凍を学ぶにあたり、以下のTakayoshi Tanakaさんのエントリーに刺激を受けてちょろっと作ってみたので公開。

銀の光と碧い空 // エクセル (xlsx形式) ファイルに張り付けられた画像ファイルを抜き出す PowerShell スクリプト

 

【どうせ作るなら】 ついでなので以下をやろうかとか

  • xlsx(エクセル)だけでなく、docx(ワード)とpptx(パワーポイント)にも対応
  • 引数を一々書くのは手間なので、バッチに対象ファイルをD&Dで画像ファイルを取得したい
  • 引数なしで実行した場合は、.bat/.ps1と同一フォルダ内の対象ファイルをターゲットに
  • せっかくなので、[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")を利用してGUIでポップアップさせてみよう
  • 例外判定も入れておくか

【ZIP化後の画像ファイル存在パス】 各Open XML形式の画像ファイルは以下のフォルダに連番で存在します。(画像はdocxをzipに拡張子変更して覗いた例)

  • エクセル : xl\media\
  • ワード : word\media\
  • パワポ : ppt\media\

 

【利用方法】 前提 : .batと.ps1を同一フォルダに置いて下さい。

  1. 対象のOpen XML形式ファイルを.batにD&D(最大9ファイル)※1
  2. .batと.ps1のあるフォルダにOpen XML形式ファイルを置いて.batを実行(ファイル数制限無し)※2

※1. バッチの引数制限です。ファイル名に以下の文字が含まれるとエラーとなります。 (禁則文字例 : &‘`'`(`)-^[]``{};)

※2. 文字制限も特にないため便利かも 【バッチソースコード全文】

@ECHO OFF
powershell .\PowerShell_Output_Media_office2007_2010.ps1 %~1 %~2 %~3 %~4 %~5 %~6 %~7 %~8 %~9

IF %ERRORLEVEL%==1 PAUSE

【PowerShellソースコード全文】 ファイル名 : PowerShell_Output_Media_office2007_2010.ps1

#Powershell 2.0
####################################
#Change Execution Policy
####################################
$CurrentPolicy = Get-ExecutionPolicy
IF (!($CurrentPolicy -eq "RemoteSigned"))
{
    Set-ExecutionPolicy RemoteSigned -WarningAction SilentlyContinue
}
Else
{
    Write-Host "Current ExecutionPolicy is $CurrentPolicy"
}

####################################
#Load .Net Windows.Forms Assembly
####################################
[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

####################################
# function : UnzipOffice2007
# param : arrayed args
# summary : get media files from Office2007 or higher xml file
####################################
function UnzipOffice2007($array){
    foreach ($inputargs in $array){

        ####################################
        # param variables list
        #
        # $input.dir = param parent directory
        # $input.file = param file name
        # $input.fullpath = param full path
        # $input.extention = param file extention
        ####################################
        $input = @{
            dir = (Split-Path $inputargs -parent);
            file = (Split-Path $inputargs -Leaf);
            }
        $input.fullpath = Join-Path $input.dir $input.file
        $input.extention = [System.IO.Path]::GetExtension($input.file)

        ####################################
        # eception - file extention and set Media path
        #
        # valid extention list
        # Excel2007 = xlsx
        # Word2007 = docx
        # PowerPoint2007 = pptx
        ####################################
        $validextention = @{ 
            excel = ".xlsx";
            word = ".docx";
            powerpoint = ".pptx";
        }

        switch ($input.extention)
         {
            $validextention.excel { $mediadir = "xl\media\" }
            $validextention.word { $mediadir = "word\media\" }
            $validextention.powerpoint { $mediadir = "ppt\media\" }
            default { throw "invalid Extention " + $validextention.Values + "`n Check target files." }
         }

        ####################################
        # target zip file info
        #
        # $zip.file = zip file name
        # $zip.fullpath = zip file fullpath
        ####################################
        $zip = @{
            file = [System.IO.Path]::ChangeExtension($input.file,".zip");
            fullpath = [System.IO.Path]::ChangeExtension($input.fullpath,".zip");
        }

        ####################################
        # output file info
        #
        # $output.tempdir = output temp folder
        # $output.finaldir = output folder(insert input file name)
        ####################################
        $output = @{
            tempfullpath = Join-Path $input.dir "media";
            finaldir = $input.file + "Media"; 
        }
        $output.finalfullpath = Join-Path $input.dir $output.finaldir

        ####################################
        # begin output Media file
        ####################################

        #check generate folder are already exist
        IF ( (Test-Path $output.finalfullpath) )
        {
            Remove-Item -Path $output.finalfullpath
        }
        IF ( (Test-Path $output.tempfullpath) )
        {
            Remove-Item -Path $output.tempfullpath
        }

        #copy arg file as zip file
        Copy-Item $inputArgs $zip.fullpath

        #Shell(Exploer)の初期化
        $comshell = New-Object -ComObject Shell.Application
        #unzip file / set media path
        $unziptarget = $comshell.Namespace($zip.fullpath).ParseName($mediadir)
        #set unzip path
        $upzippath = $comshell.NameSpace($input.dir)
        #output gathered items in the media folder
        $unziptarget | ForEach-Object { $upzippath.CopyHere($_.Path) }

        #remote copied zip file
        Remove-Item $zip.fullpath
        #add file name into media folder name
        Rename-Item $output.tempfullpath $output.finaldir
        #open media folder
        $output.finalfullpath | Invoke-Item

    }
}


####################################
# begin check target file
#
# summary : change target by args exists or not
# if args exist : remove " " from arg file name
#   $fullpaths = fullpath with " " removed from arg file
#   $filelist = target file list after removed " "
# if args mpt exist : set current path's docx,exlsx,pptx as target file
#   $fullpaths = fullpath array
####################################
#create temp array to import checked args
$fullpaths=@()
IF ($args.Length -gt 0)
{
    foreach ($arg in $args){
        #check path exist as args split by " "
        $temppath += $arg
        if (test-path $temppath)
        {
            $fullpaths += $temppath
            $temppath = ""
        }
        Else
        {
            #remove " "
            $temppath += " "
        }
    }
    IF ($fullpaths.Length -eq 0) 
    {
        #if error char exist
        [void][System.Windows.Forms.MessageBox]::Show("ドラッグした対象ファイル名に次の文字が含まれています。
        `n方法1. ファイル名から禁則文字を削除する。(禁則文字例 : &‘`'`(`)-^[]``{};)
        `n `t`t`t or 
        `n方法2. 対象ファイルが存在するフォルダで直接バッチファイルを実行する。"
        ,"ファイル名取得エラー") 
    }
    Else
    {
        #show file list
        $fileList = $fullpaths | foreach {$_ + "`n"}
        [void][System.Windows.Forms.MessageBox]::Show($FileList, "画像抽出ファイル一覧(バッチ制限=最大9ファイル)")
    }
}
Else
{
    #get current path
    $currentpath = Get-Location
    #obtain target Office2007 files and set fullpath
    $fullpaths += Get-ChildItem -Name -Include *.docx,*.xlsx,*.pptx |ForEach-Object {Join-Path $currentpath $_}
    #show target file inside currenct folder
    IF (!($fullpaths.Length -eq 0))
    {
    [void][System.Windows.Forms.MessageBox]::Show($fullpaths, "画像抽出ファイル一覧(バッチ制限=最大9ファイル)")
    }
    Else
    {
    [void][System.Windows.Forms.MessageBox]::Show("Office2007以上のXMLファイルが存在しません。
    `n方法1.ドラッグアンドドロップする
    `n方法2.対象ファイルの存在するフォルダで直接バッチファイルを実行して下さい。"
    , "情報")
    }
}


####################################
# Main
####################################

UnzipOffice2007 $fullpaths