どうもミツシマです。


最近、社内のファイルサーバをSharePoint Onlineへ移行して使用している会社も増えてきているのではないでしょうか?私が勤めている会社もSharePoint Onlineを使い始めた。
そんなある日、「あるサイト上にある特定のファイルを検索して、条件にあったものをダウンロードする」という必要があった。フォルダやファイルの数が少なければ目で見てやればいいのだが、さすがにそんな訳に行かない量が既にあったので、コマンドで検索・ダウンロード出来ないかを模索。

そんな訳で今回はPowershellコマンドにてサイト上のファイル検索とダウンロードを検証した内容を記載したいと思う。


検証環境は以下の通り
PC:Windows10 Pro


〜SharePoint CSOMを使用する準備〜

今回SharePoint OnlineをPowershell上でリモート操作する為にSharePoint CSOMを使用した。
これは、Nuget版とダウンロード版がある。
今回は諸事情でダウンロード版でやっているが、可能であればNuget版が良いみたいだ。

詳しい手順は↓↓↓
PowerShell で SharePoint CSOM を使用する際の Tips


準備が出来たらいざスクリプトの作成!!!


色々あって、本当に色々あって出来たスクリプトがこちら↓↓↓


〜完成したスクリプト〜

SPO-FindFile.ps1
#===メイン関数===
Function FindFile($FolderURLPath,$FileType){

    #変数の初期化
    $TargetFolderURL = ""

    Write-Output $FolderURLPath

    #対象フォルダー取得
    $objFolder = $Context.Web.GetFolderByServerRelativeUrl($FolderURLPath)
    $Context.Load($objFolder)
    $Context.ExecuteQuery()

    #フォルダー配下のファイルコレクションを取得
    $Files = $objFolder.Files
    $Context.Load($Files)
    $Context.ExecuteQuery()

    #ファイルが存在した場合、探してるファイルかどうかをチェック
    If ($Files.count -ge 1){
        foreach($File in $Files){
        If ($File.Name -like $FileType){
                $FileName = $File.Name
                $FileCreatedTime = $File.TimeCreated
                $FilePath = $FolderURLPath + "/" + $FileName
                $OutputText = $FilePath + "," + $FileCreatedTime
                $OutputText | Out-File $OutputCSVPath -encoding UTF8 -append
            }
        }
    }
    
    #サブフォルダ-を取得
    $objSubFolders = $objFolder.Folders
    $Context.Load($objSubFolders)
    $Context.ExecuteQuery()

    #サブフォルダーが存在した場合、再帰処理実行
    If ($objSubFolders.Count -ge 1){
        foreach($objSubFolder in $objSubFolders){
            [String]$objSubFolderName = $objSubFolder.Name
            #Write-Output $objSubFolderName
            $TargetFolderURL = $FolderURLPath + "/" + $objSubFolderName

            #メイン関数実行
            FindFile $TargetFolderURL $FindFileType
        }
    }
}
#===メイン関数ここまで===



#SharepointOnlineに繋ぐためのアセンブリのロード
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client") > $null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime") > $null

#事前環境変数 設定
$UserName = "test01@<ドメイン>.onmicrosoft.com"
$Password = "Password"
$SiteURL = "https://<ドメイン>.sharepoint.com/sites/TEST-Sites"
$StaticURL = "/TESTDocuments"
$FindFileType = "*.txt"

$CurrentFolder = Split-Path $MyInvocation.MyCommand.Path -Parent
$OutputCSVPath = $CurrentFolder + "\FindFileList.csv"


#サイトへの認証処理
$pwd = convertto-securestring $Password -AsPlainText -Force
$credential = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName, $pwd)
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
$Context.Credentials = $credential

$objWeb = $Context.Web
$Context.Load($objWeb)
$Context.ExecuteQuery()

#CSVファイルの準備
$StaticText = "FileURL,FileTimeStamp"
$StaticText | Out-File $OutputCSVPath -encoding UTF8 -append


$TargetFolderURL = $SiteURL + $StaticURL

#メイン関数実行
FindFile $TargetFolderURL $FindFileType


DownloadSPO-File.ps1
#SharepointOnlineに繋ぐためのアセンブリのロード
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client") > $null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime") > $null

#事前環境変数 設定
$CurrentFolder = Split-Path $MyInvocation.MyCommand.Path -Parent
$CSVPath = $CurrentFolder + "\FindFileList.csv"
$FileURLLists = Import-Csv -Path $CSVPath -Encoding Default
$OutputFolderPath = $CurrentFolder + "\Download-Files\"

$UserName = "test01@<ドメイン>.onmicrosoft.com"
$Password = "Password
$SiteURL = "https://<ドメイン>.sharepoint.com/sites/TEST-Sites"
$StaticURL = "/TESTDocuments"

#ダウンロードフォルダがなければ、作成
If(-Not(Test-Path $OutputFolderPath)){
    Write-Output "「Download-Files」フォルダがない為、作成します."
    New-Item $OutputFolderPath -ItemType Directory
}


#サイトへの認証処理
$pwd = convertto-securestring $Password -AsPlainText -Force
$credential = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName, $pwd)
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
$Context.Credentials = $credential

$objWeb = $Context.Web
$Context.Load($objWeb)
$Context.ExecuteQuery()


foreach($FileList in $FileURLLists){
    
    $FileFullPath = $FileList.FileURL
    Write-Output "ファイルフルパス(URL):$FileFullPath"

    $intFileCurrentPathStart = $FileFullPath.Indexof($StaticURL) + $StaticURL.Length
    $intFileCurrentPathEnd = $FileFullPath.Length
    #Write-Output $intFileCurrentPathStart
    #Write-Output $intFileCurrentPathEnd

    $FilePath = $FileFullPath.Substring($intFileCurrentPathStart,($intFileCurrentPathEnd - $intFileCurrentPathStart))
    Write-Output "ファイルカレントパス(URL):$FilePath"

    $intFileNameStart = $FileFullPath.LastIndexOf('/') + 1
    $intFleNameEnd = $FileFullPath.Length
    #Write-Output $intFileNameStart
    #Write-Output $intFleNameEnd

    $FileName = $FileFullPath.Substring($intFileNameStart,($intFleNameEnd - $intFileNameStart))
    Write-Output "ファイル名:$FileName"

    $SPOFile = $objWeb.ServerRelativeUrl + $StaticURL + $FilePath
    $SaveFilePath = $OutputFolderPath + $FileName
    #Write-Output $SaveFilePath

    # sharepoint上のファイルを開いてからローカルファイルの書き込みストリームを開き、データコピーを行う
    $OpenFile = [Microsoft.SharePoint.Client.File]::OpenBinaryDirect($Context, $SPOFile)
    $WriteStream = [System.IO.File]::Open($SaveFilePath,[System.IO.FileMode]::Create)
    $OpenFile.Stream.CopyTo($WriteStream)

    $WriteStream.Close()
}


〜解説〜

まず今回は「ファイルの検索」と「検索したファイルのダウンロード」をするスクリプトの2つを作成した。もし一回で済ませたいという人がいれば、2つのスクリプト「SPO-FindFile.ps1」と「DownloadSPO-File.ps1」を組み合わせれば出来ると思う。

まずは「SPO-FindFile.ps1」の解説から。
まず必ず実行するのが、SharePoint Online CSOMを使用する為にアセンプリをロードすること。これは毎回実行が必要。
次にSharePoint Onlineに接続するための「ユーザー情報」・「サイト情報」・「検索するファイルの情報」を環境変数で定義している。この辺りの情報を変更すれば違うサイトやファイルの検索条件を変更出来る。
(ここでは「.txt」というファイルを検索するようにしている。本当は正規表現とかを使用すればもっと細かく検索出来ると思うが、あまり使用した経験がないので断念。。。。orz)
また、独自ドメインを取得されている場合にはそのドメイン名でOK。
テスト環境のため「〜.onmicrosoft.com」というドメインを今回は使用している。

このスクリプトを作成することでSharePoint Onlineのサイト上でパスがどのように表示されるかすごく勉強になった。
基本的には「https://<ドメイン>.sharepoint.com/sites/<サイト名>/<ドキュメント名>/<各フォルダ階層〜〜>」みたいな感じである。(サイトを作成すると最初からある「共有ドキュメント」を使用すると「Share〜」みたいなドキュメント名になる。)
今回のスクリプト上では「TEST-Sites」を作成した上でドキュメント「TESTDocuments」の中を検索している。

そして実行したスクリプトがあるフォルダに「FindFileList.csv」というファイルを作成し、そこに検索でヒットしたファイルのURLとタイムスタンプを書き出していく感じだ。


基本的にファイルの検索は「FindFile」という関数を作成して、そこでサブフォルダーも含めて検索する為に再帰的に実行させている。


次に「DownloadSPO-File.ps1」の解説
先程実行して作成された「FindFileList.csv」の内容からそれぞれダウンロードしているという流れ。
ダウンロード先はスクリプトが配置されているフォルダに「Download-Files」というフォルダを作成し、そこにダウンロードする形である。
注意点としては既にファイルがSharePoint Online上に存在しない場合、0KBファイルが生成される。
また、「Download-Files」フォルダに同名のファイルが存在する場合は上書きされてしまうので、実行する前には「Download-Files」フォルダごと削除するか、中身を空にして欲しい。



一応やりたいことは出来たので今回の検証は終了。

P.S.今後Office365がどんどん活用されていくはずなので、個人的に勉強していきたいと思う(^^)

スポンサードリンク