VBAでcertutilをコールしてBase64エンコードする方法

Webページやhtmlメールで表示されている画像の多くは、どこかのサーバーに置かれているファイルへのリンクになっているかと思う。リンクが多いと表示するのに通信が多発して時間がかかるかもしれない。また、どうしても画像リンクでは何かしらの要求を満たせないかもしれない。そんな場合、画像をテキスト化してhtmlソースに貼り付けることで対応可能だ。

ここでは、certutil の エンコード機能を使ってテキスト化する流れを見ていく。言語は私が使い慣れているExcel VBAとする。まず、Base64エンコードするサンプルコードを以下に示す。なお、参照設定で「Windows Script Host Object Model」を追加しておく必要がある。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
' Base64変換処理
Private Function runEncode(imagePath, imageTextPath)
    Dim fso
    Dim wsh, wExec

    On Error GoTo ErrorHandler

    Set fso = CreateObject("Scripting.FileSystemObject")

    ' 画像ファイルをBase64形式へ変換
    With fso.CreateTextFile(ThisWorkbook.path & "\encode.bat")
        .Write "certutil -encode -f " & imagePath & " " & imageTextPath
        .Close
    End With
    Set wsh = CreateObject("WScript.Shell")
    Set wExec = wsh.Exec(ThisWorkbook.path & "\encode.bat")

    ' コマンド実行中は待ち
    Do While wExec.Status = WshRunning Or wExec.Status = WshFailed
        ' コマンド失敗時
        If wExec.Status = WshFailed Then
            Debug.Print "certutil error!"
            runEncode = -1
            Exit Function
        End If

        Call Sleep(100)
        DoEvents
    Loop

    ' 後処理
    Set fso = Nothing
    Set wsh = Nothing
    Set wExec = Nothing

    runEncode = 1
    Exit Function
ErrorHandler:
    Debug.Print "imageToText error! Err.Number=[" & Err.Number & "], Err.Description=[" & Err.Description & "]"
    runEncode = -1
End Function

流れとしては、batファイルを作ってcertutilコマンドを書き込み、そのbatファイルを実行している。環境によっては、batファイルを作らずに、直接WSHなどからcertutilを実行できるはず(以前はできた)。だが、今の私の環境ではUATがらみの制限に引っかかっているようで、どうしても正常に動作してくれない。試しにbatファイルにコマンドを落として実行してみたら、偶然うまくいった…というだけだ。

どうしてもbatファイルを経由したくない場合、直接certutilを実行する方法も一応ある。それはrunasを使って管理者権限で実行する方法だ。例えば上記ソースの8行目~14行目をコメント化して、16行目を以下のように変えてみる。(下記コードのuserは管理者権限を持っている必要があるかもしれない。試していないので分からない。)

Set wExec = wsh.Exec("runas /savecred /user:user ""cmd.exe /c certutil -encode -f " & imagePath & " " & imageTextPath & """")

ただしこれだけでは正常に動かない。事前に資格情報を登録する必要がある。そのための一つの方法は、上記コードのrunasコマンドの部分をいったんbatファイルに書き出して保存し、それを実行する。そのときにuserに対するパスワードを聞かれるので、入力する。これにて資格情報が登録される。登録は最初の1回でOKだ。 ここまでして、ようやく上のコードが動く。しかし自分一人が使う分にはまだ我慢できるが、とても他の人に使ってもらえるような代物ではない。最初に提示したコードのほうが汎用性があって、まだマシだ。

脱線気味だが元に戻って、次にcertutilのencodeで出力されたファイルは下図のようになっており、そのままでは使えない。無駄なヘッダーやフッターがあり、また、見栄えよく改行されているからだ。

図1.certutilコマンドの encodeオプションで出力されたファイルの様子

そこで、これらを取り除く処理が必要となる。以下コードを参照。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<pre class="brush: vb; class-name: &#039;wp-block-code&#039;; title: ; notranslate" title="">' テキストファイル中の文字を削除・置き換える
Private Function getAfTxt(imageTextPath) As String
    Dim fso, txt, buf

    ' ファイルシステムオブジェクトを生成してファイルを開く
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set txt = fso.OpenTextFile(imageTextPath, ForReading)

    ' 全文読み込み
    buf = txt.ReadAll

    ' 置換処理
    buf = Replace(buf, "-----BEGIN CERTIFICATE-----", "")
    buf = Replace(buf, "-----END CERTIFICATE-----", "")
    buf = Replace(buf, vbCrLf, "")

    ' 後処理
    txt.Close
    Set txt = Nothing
    Set fso = Nothing
    getAfTxt = buf
End Function

以上で準備OK。あとは上記二つの関数を呼び出すだけでOKだ。 ついでにhtmlファイルも作成してみる。以下コード参照。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Sub sample()
    Dim imagePath, imageTextPath, imageText
    Dim fso

    '変換後テキストのフルパス設定
    imagePath = "C:\Users\user\Pictures\sample.jpg"
    imageTextPath = imagePath & ".txt"

    ' Base64変換処理の呼び出し
    Call runEncode(imagePath, imageTextPath)

    ' Base64変換後テキストの取得
    imageText = getAfTxt(imageTextPath)

    ' htmlファイルを作成してimgタグのsrc属性にBase64変換後テキストを設定する
    Set fso = CreateObject("Scripting.FileSystemObject")
    With fso.CreateTextFile( _
            FileName:="C:\Users\user\Desktop\sample.html", _
            overwrite:=True, _
            Unicode:=False)
        .WriteLine ("&lt;img src=""data:image/png;base64," & imageText & """&gt;")
    End With

    Set fso = Nothing
End Sub

これを実行して作成されたファイルをブラウザで開いてみる。htmlソースも表示してみる。字が小さくて少しわかりずらいが、imageタグのsrc属性がURLリンクでなく、テキスト(の一部)が設定されていることが確認できる。

図2.作成されたhmltファイルとhtmlソース

最後に、Base64エンコードされたテキストファイルは、もとの画像ファイルより少しサイズが大きくなる。よって、画像ファイルをテキスト化してブラウザなどで表示すると、今度は通信ではなくブラウザ自体が表示遅延のボトルネックになってくる。あまり大きな画像を扱わないほうが無難かもしれない。

参考ページ

https://qiita.com/halpas/items/2296cf611a6370f640a3

関連ページ