2025/01/05

作ってみよう:#18)スマート名刺管理 - GTP4o API リクエストの作成 ②

GPT4o に画像を送信する JSON フォーマットで、赤線の部分が画像ファイルを Base64 でエンコードした文字列でした。この文字列を作成する部分を解説します。


画像データの取得方針

リッチテキストライトフィールドに Nomad から写真を添付する方法は、写真と添付ファイルの 2 種類の方法があります。写真を選択すると添付ファイルになります。カメラから撮影すると貼り付ける際にイメージの縮小が可能です。画像サイズを小さくしたいとき有効なのですが、この方法だとリッチテキストにインラインイメージ(リッチテキストに見える状態で貼り付けた画像)となります。

そこで今回は、添付ファイルとインラインイメージの双方に対応したいと思います。

ただ、インラインイメージは LotusScript の通常の NotesRichText*** クラスではアクセスできません。そこで、今回は DXL を使用して、リッチテキスト内のデータを取得します。

このシリーズにおいて、この記事だけは JSON ではなく DXL について記載しています。ご注意ください。なお、DXL を使用して、画像や添付ファイルを取得する方法については以下のリンクを参照ください。


画像取得関数

画像データを取得する関数は、大きく 2 つの処理に分かれています。まず、添付ファイルを探し、見つからない場合は、インラインイメージを取得しています。

戻り値は、取得した画像の Base64 文字列となります。

Private Function xGetImage_Base64(vnd As NotesDocument, ByVal vsFldName As String) As String
   '最初の添付ファイルを Base64 文字列で取得
   xGetImage_Base64 = xGetDXL_FirstAttachment64(vnd, "Body")

   If xGetImage_Base64 = "" Then
      '添付はない(= 最初のインラインイメージを取得)
      xGetImage_Base64 = xGetDXL_FirstInlineImage64(vnd, "Body")
   End If
End Function

なお、添付ファイルを先にチェックしているには理由があります。

添付ファイルを DXL に変換するとそこにアイコンの画像が含まれます。この画像がインラインイメージと同じ構造となっています。先にインラインイメージを検索すると添付ファイルのアイコン画像を取得してしまう可能性があるからです。


最初の添付ファイルの取得

DXL Step-by-Step:#46)添付ファイルの取得 ① で紹介した関数とほぼ同じです。

ただ、API に送信するのも Base64 エンコード、DXL から取得するのも同じです。リンクの記事の関数はデコードしてバイナリデータで返す仕様だったのですが、そこを省き、取得した Base64 文字列をそのまま戻り値として返すようにしています。

Private Function xGetDXL_FirstAttachment64(vnd As NotesDocument, ByVal vsFld As String) As String
   'DXL の準備
   Dim dprs As NotesDOMParser
   Set dprs = xGetDOMParser(vnd)

   'DOM ツリーのルートを取得
   Dim ddn As NotesDOMDocumentNode
   Set ddn = dprs.Document

   'リッチテキストの取得
   Dim denRT As NotesDOMElementNode
   Set denRT = xGetDXL_item(ddn, vsFld)

   '添付情報の取得
   Dim denRef As NotesDOMElementNode
   Set denRef = xGetDXL_FirstNodeByName(denRT, "attachmentref")

   If Not (denRef Is Nothing) Then
      'ファイル名取得
      Dim sName As String
      Dim sDisp As String
      sName = denRef.GetAttribute("name")
      sDisp = denRef.GetAttribute("displayname")

      '$FILE フィールド取得
      Dim denFile As NotesDOMElementNode
      Set denFile = xGetDXL_FileItem(ddn, sName) 'Notes 内部の名称で検索

      If Not (denFile Is Nothing) Then
         'ファイルの中身を取得
         Dim denData As NotesDOMElementNode
         Set denData = xGetDXL_FirstNodeByName(denFile, "filedata")

         If Not (denData Is Nothing) Then
            Dim dtn As NotesDOMTextNode
            Dim sB64 As String

            'エンコードされたファイルを取得
            Set dtn = denData.FirstChild
            sB64 = dtn.NodeValue

            '戻り値セット
            xGetDXL_FirstAttachment64 = sB64
         End If
      End If
   End If
End Function


最初のインラインイメージの取得

こちらも #42)インラインイメージの取得 で紹介した関数とほぼ同じなのですが、上記度同様に、取得した Base64 文字列をそのまま戻り値として返すようにしています。

Private Function xGetDXL_FirstInlineImage64(vnd As NotesDocument, ByVal vsFld As String) As String
   'DXL の準備
   Dim dprs As NotesDOMParser
   Set dprs = xGetDOMParser(vnd)

   'DOM ツリーのルートを取得
   Dim ddn As NotesDOMDocumentNode
   Set ddn = dprs.Document

   'リッチテキストの取得
   Dim denRT As NotesDOMElementNode
   Set denRT = xGetDXL_item(ddn, vsFld)

   '画像の取得
   Dim denPct As NotesDOMElementNode
   Set denPct = xGetDXL_FirstNodeByName(denRT, "picture")

   '画像ファイル取得
   Dim denImg As NotesDOMElementNode
   Dim dnl As NotesDOMNodeList
   Dim dtn As NotesDOMTextNode
   Dim sB64 As String

   Dim asTag(2) As String
   asTag(0) = "png"
   asTag(1) = "gif"
   asTag(2) = "jpeg"

   '画像データの捜索
   If Not(denPct Is Nothing) Then
      ForAll sTag In asTag
         Set denImg = xGetDXL_FirstNodeByName(denPct, sTag)
         If Not(denImg Is Nothing) Then
            'エンコードされた画像データ取得
            Set dtn = denImg.FirstChild
            sB64 = dtn.NodeValue

            '戻り値セット
            xGetDXL_FirstInlineImage64 = sB64
            Exit ForAll
         End If
      End ForAll
   End If
End Function


その他サブ関数

上記 2 関数からコールされている関数群を列挙します。こちらにまとめた関数は過去の記事の関数をそのまま再掲しています。

◇ 文書を DXL に変換

Private Function xGetDOMParser(vnd As NotesDocument) As NotesDOMParser
   'Dominoデータ を DXL に変換する準備
   Dim dexp As NotesDXLExporter
   Set dexp = xns.CreateDXLExporter()
   Call dexp.SetInput(vnd)

   'パーサーに変換する DXL をセット
   Dim dprs As NotesDOMParser
   Set dprs = xns.CreateDOMParser()
   Call dprs.SetInput(dexp)

   'DXL 変換を実行
   Call dexp.Process()

   Set xGetDOMParser = dprs
End Function

◇ 検索して最初のノードを取得

Private Function xGetDXL_FirstNodeByName(vdenParent As NotesDOMElementNode, ByVal vsName As String) As NotesDOMElementNode
   Dim dnl As NotesDOMNodeList

   On Error Resume Next

   '配下のノードを検索
   Set dnl = vdenParent.GetElementsByTagName(vsName)
   If dnl.NumberOfEntries > 0 Then
      '最初のノードを戻り値にセット
      Set xGetDXL_FirstNodeByName = dnl.GetItem(1)
   End If
End Function

◇ フィールドを検索して取得

Private Function xGetDXL_item(vddn As NotesDOMDocumentNode, ByVal vsFld As String) As NotesDOMElementNode
   '文書のノードの取得
   Dim denDoc As NotesDOMElementNode
   Set denDoc = vddn.DocumentElement

   'itemノードをチェック
   Dim den As NotesDOMElementNode
   Dim dnl As NotesDOMNodeList
   Dim i As Integer
   Dim sFld As String

   Set dnl = denDoc.GetElementsByTagName("item")
   For i = 1 To dnl.NumberOfEntries
      Set den = dnl.GetItem(i)

      sFld = den.GetAttribute("name")
      If LCase(sFld) = LCase(vsFld) Then
         '戻り値セットしてループ脱出
         Set xGetDXL_item = den
         Exit For
      End If
   Next
End Function

◇ 添付ファイルを検索して取得

Private Function xGetDXL_FileItem(vddn As NotesDOMDocumentNode, ByVal vsName As String) As NotesDOMElementNode
   '文書のノードの取得
   Dim denDoc As NotesDOMElementNode
   Set denDoc = vddn.DocumentElement

   'itemノードをチェック
   Dim denItem As NotesDOMElementNode
   Dim dnl As NotesDOMNodeList
   Dim i As Integer
   Dim sFld As String
   Dim sName As String
   Dim den As NotesDOMElementNode

   Set dnl = denDoc.GetElementsByTagName("item")
   For i = 1 To dnl.NumberOfEntries
      Set denItem = dnl.GetItem(i)
      sFld = denItem.GetAttribute("name")
      If LCase(sFld) = LCase("$FILE") Then
         '添付ファイルなのでチェックを継続
         'object ノード取得
         Set den = xGetDXL_FirstNodeByName(denItem, "object")
         If Not (den Is Nothing) Then
            'file ノード取得
            Set den = xGetDXL_FirstNodeByName(den, "file")
            If Not (den Is Nothing) Then
               '添付ファイルなので Notes 内部のファイル名取得
               sName = den.GetAttribute("name")
               If LCase(sName) = LCase(vsName) Then
                  'ファイル名一致
                  '戻り値セットしてループ脱出

                  Set xGetDXL_FileItem = den 'file ノード
                  Exit For
               End If
            End If
         End If
      End If
   Next
End Function


前回 作ってみよう 次回


0 件のコメント:

コメントを投稿