これは何がうれしいかというとEXCELのマクロブックとかをメールに添付して送りたくない場合に使う手法です。
①マクロブックをB64変換を行う
②メールでマクロを送りたい人にB64テキストファイルを添付して送る
③メールを受け取った側はB64復元でB64テキストをマクロブックに戻す。
一連の処理コードを以下に添付します。
Option Explicit
'=============================================================
' <参考>
' ファイルをBase64エンコード・デコードするVBAマクロ
' https://www.ka-net.org/blog/?p=4479
'=============================================================
'-----------------------------------------------------------------
' Sub ■B64変換()
' 概要:指定のファイルをB64形式のファイルへ返還する
'-----------------------------------------------------------------
' Sub ■B64復元()
' 概要:B64形式のファイルを元のファイルへ返還する
'-----------------------------------------------------------------
Sub ■B64変換()
Dim Start
Start = Timer
On Error GoTo Catch
Dim WS0 As Worksheet
Set WS0 = ActiveSheet
Dim 開始 As String
開始 = Format(Now, "yyyy/mm/dd hh:mm:ss")
WS0.Cells(1, "B") = "開始:" & Format(Now, "yyyy/mm/dd hh:mm:ss")
Dim strFiles As String
Dim i As Long, j As Long, k As Long, m As Long
Dim QMSG As String, MSG As String
Dim RC
QMSG = "B64変換対象ファイルを指定して下さい?"
RC = MsgBox(QMSG, vbYesNo, "■変換対象ファイルの選択")
Select Case RC
Case vbYes
Case Else
MsgBox "処理を中止します。", vbCritical
End
End Select
'1.返還前のファイルを指定する
strFiles = ファイル選択処理
If strFiles = "" Then
MsgBox "処理を終了します!" & vbLf & vbLf & "もう一度起動ボタンクリックから実行願います。"
Exit Sub
End If
Dim sFiles
Dim eRow As Long
sFiles = Split(strFiles, vbLf)
Dim fullPath As String
Dim folderPath As String
Dim sFile As String
j = 1
fullPath = sFiles(j) 'ファイル名は配列の添え字1からセットされる
folderPath = Left(fullPath, InStrRev(fullPath, "\"))
sFile = Mid(fullPath, InStrRev(fullPath, "\") + 1)
WS0.Cells(3, "A") = sFile
WS0.Cells(3, "B") = fullPath
'2.返還後のファイルをファイル名の最後に「_B64.txt」付与する。
WS0.Cells(3, "C") = sFile & "_B64.txt"
WS0.Cells(3, "D") = folderPath & sFile & "_B64.txt"
B64変換処理:
Dim TTL As String
fullPath = WS0.Cells(3, "B")
TTL = EncodeBase64FromFile(fullPath)
'テキスト変換してファイルにする
Open WS0.Cells(3, "D") For Output As #1
Print #1, TTL;
Close #1
'---------------------------------------------------------------------------
'別途、サイズが73*13000=949000以上ならば10000行で区切ってファイル分割を行う。
'理由:メールの添付ファイルサイズが1M以内の制約がある場合を想定・・・
'---------------------------------------------------------------------------
Dim 分割サイズ As Long
Dim 分割ファイル行数 As Long
Dim 分割行数 As Long
Select Case True
Case WS0.Cells(3, "E") = ""
分割ファイル行数 = 13000
Case IsNumeric(WS0.Cells(3, "E"))
分割ファイル行数 = CLng(WS0.Cells(3, "E"))
Case Else
分割ファイル行数 = 13000
End Select
分割サイズ = 分割ファイル行数 * 73
Select Case True
Case WS0.Cells(3, "F") = ""
分割行数 = 10000
Case IsNumeric(WS0.Cells(3, "F"))
分割行数 = CLng(WS0.Cells(3, "F"))
Case Else
分割行数 = 10000
End Select
If Len(TTL) > 分割サイズ Then
Call B64ファイル分割2(TTL, WS0.Cells(3, "D"), 分割行数)
MSG = "B64ファイルのサイズが " & Format(分割サイズ, "#,##0") & " を超えたため"
MSG = MSG & vbLf & "分割ファイルを作成しています。"
Else
MSG = ""
End If
Dim 終了 As String
終了 = Format(Now, "yyyy/mm/dd hh:mm:ss")
Dim 開始終了 As String
開始終了 = WS0.Cells(1, "B")
開始終了 = 開始終了 & vbLf & "終了:" & Format(Now, "yyyy/mm/dd hh:mm:ss")
開始終了 = 開始終了 & vbLf & "処理:" & Format(CDate(終了) - CDate(開始), "hh:mm:ss ") & Format(Timer - Start, "00.0秒")
WS0.Cells(1, "B") = 開始終了
MsgBox "B64変換処理が終了しました!" & vbLf & vbLf & MSG
Finally:
Exit Sub
Catch:
Debug.Print Err.Number, Err.Description
Stop
Resume
End Sub
Sub ■B64復元()
'1.B64変換後のファイルを指定する
'2.変換後ファイルの"_B64.txt"を取り除いたファイルを復元ファイル名とする
'3.変換後ファイル名が"_B64_001.txt"の場合はそのほかの分割ファイルをマージして
' 変換後ファイル"_B64.txt"を作成する
Dim Start
Start = Timer
On Error GoTo Catch
'1. ファイル選択ダイアログでファイルを選択してシートの所定のカラムにセットする
Dim WS0 As Worksheet: Set WS0 = ActiveSheet
Dim 開始 As String: 開始 = Format(Now, "yyyy/mm/dd hh:mm:ss")
WS0.Cells(5, "B") = "開始:" & Format(Now, "yy/mm/dd hh:mm:ss")
Dim i As Long, j As Long, k As Long, m As Long
Dim strFiles As String
Dim QMSG As String, MSG As String
Dim RC
QMSG = "変換後B64ファイルを指定して下さい?"
RC = MsgBox(QMSG, vbYesNo, "■変換後ファイルの選択")
Select Case RC
Case vbYes
Case Else
MsgBox "処理を中止します。", vbCritical
Exit Sub
End Select
strFiles = ファイル選択処理
If strFiles = "" Then
MsgBox "処理を終了します!" & vbLf & vbLf & "もう一度起動ボタンクリックから実行願います。"
Exit Sub
End If
Dim sFiles
Dim eRow As Long
sFiles = Split(strFiles, vbLf)
Dim fullPath As String
Dim folderPath As String
Dim sFile As String
j = 1
fullPath = sFiles(j)
folderPath = Left(fullPath, InStrRev(fullPath, "\"))
sFile = Mid(fullPath, InStrRev(fullPath, "\") + 1)
'「_B64_001.txt」は分割ファイルの最初のファイルを表す
If Right(sFile, 12) = "_B64_001.txt" Then
'① FSOにてファイルリストの中からLIKE演算子を使い分割ファイル名を探し
'② ファイル一覧をソートしてソート順にファイルのマージ処理を行う
Dim sCombineFileName As String
Dim seqFile As String
sCombineFileName = Left(sFile, Len(sFile) - 8) & ".txt"
seqFile = Left(sFile, Len(sFile) - 7) & "*" & ".txt"
Call combineFiles1(folderPath, seqFile, sCombineFileName)
Else
sCombineFileName = sFile
End If
WS0.Cells(7, "A") = sCombineFileName
WS0.Cells(7, "B") = folderPath & sCombineFileName
WS0.Cells(7, "C") = Left(sCombineFileName, Len(sCombineFileName) - 8)
WS0.Cells(7, "D") = folderPath & Left(sCombineFileName, Len(sCombineFileName) - 8)
B64復元処理:
'B64変換後ファイルを読み込む
Dim L1 As String
Dim TTL As String
i = 0
Dim text_path As String
text_path = WS0.Cells(7, "B")
''テキストファイルを開く
'Open text_path For Input As #1
'
''最終行までループ
'Do Until EOF(1)
' Line Input #1, L1 '毎回1行読み込み
' i = i + 1
' TTL = TTL & L1 'B64文字列を連結
'Loop
'
'Close #1
Dim FSO
Dim oFile As Object
Set FSO = CreateObject("Scripting.FileSystemObject")
'ファイルを開く
Dim oTS As Object
Set oTS = FSO.OpenTextFile(text_path, ForReading)
'データを全て新規ファイルに書き込み
Dim sRead As String
sRead = oTS.ReadAll
TTL = Replace(sRead, vbLf, "")
Dim TB As Object
'Set TB = WS0.Shapes("テキスト ボックス 1").TextFrame
If Len(TTL) >= (10 * 1000000) Then
'サイズが大きいと処理が完了しなくなる。
'10Mバイトごとにバイナリー変換してできれば連結していけるようにする
End If
fullPath = WS0.Cells(7, "D")
Dim IMSG As String
If DecodeBase64ToFile(TTL, fullPath) = 0 Then
IMSG = "B64復元処理に失敗しました!(SIZE=" & Len(TTL)
Else
IMSG = "B64復元処理が成功しました!"
End If
Dim 終了 As String
終了 = Format(Now, "yyyy/mm/dd hh:mm:ss")
Dim 開始終了 As String
開始終了 = WS0.Cells(1, "B")
開始終了 = 開始終了 & vbLf & "終了:" & Format(Now, "yyyy/mm/dd hh:mm:ss")
開始終了 = 開始終了 & vbLf & "処理:" & Format(CDate(終了) - CDate(開始), "hh:mm:ss ") & Format(Timer - Start, "00.0秒")
WS0.Cells(5, "B") = 開始終了
MsgBox IMSG, vbInformation
Finally:
Exit Sub
Catch:
Debug.Print Err.Number, Err.Description
Stop
Resume
End Sub
''新規に出力ファイルを作成する。
'intFileNumber = FreeFile 'ファイルの空き番号を得る
''バイナリファイルを作成
'Open strSaveFileName For Binary As intFileNumber
'Seek #intFileNumber, LOF(intFileNumber) + 1&
'
''バッファの内容をファイルに書き込む
'Put #intFileNumber, , bytSaveText
''ファイルを閉じる
'Close #intFileNumber
'【エクセルVBA】TextFrameオブジェクトとCharactersオブジェクトでテキストボックスを操作してみよう!
'https://tonari-it.com/excel-vba-textframe-charcters/
'
'Sub Text_Box_Change()
' With ActiveSheet.Shapes("テキスト ボックス 1").TextFrame
' .Characters.text = "テキスト更新!"
' .HorizontalAlignment = xlHAlignRight
' .VerticalAlignment = xlVAlignCenter
' End With
'
'End Sub
'
'
Function ファイル選択処理() As String
Dim strFiles As String
Dim i As Long
ファイル選択処理 = ""
With Application.FileDialog(msoFileDialogFilePicker)
'ファイルの複数選択を可能にする
.AllowMultiSelect = False
'ファイルフィルタのクリア
.Filters.Clear
'ファイルフィルタの追加
.Filters.Add "ZIP,TEXT,PDF,その他", "*.txt; *.*"
.Filters.Add "エクセルブック他", "*.xls*; *.doc*"
'初期表示フォルダの設定
.InitialFileName = ThisWorkbook.Path & "\"
If .Show = -1 Then 'ファイルダイアログ表示
' [ OK ] ボタンが押された場合
For i = 1 To .SelectedItems.Count
strFiles = strFiles & vbLf & .SelectedItems(i)
Next i
'MsgBox "以下のファイルが選択されました。" & _
vbLf & strFiles, vbInformation
Else
' [ キャンセル ] ボタンが押された場合
MsgBox "ファイル選択がキャンセルされました。", vbExclamation
ファイル選択処理 = ""
Exit Function
End If
End With
ファイル選択処理 = strFiles
Finally:
Exit Function
Catch:
Debug.Print Err.Number, Err.Description
Stop
Resume
End Function
Private Function EncodeBase64FromFile(ByVal FilePath As String) As String
'ファイルをBase64エンコード
Dim elm As Object
Dim ret As String
Const adTypeBinary = 1
Const adReadAll = -1
ret = "" '初期化
On Error Resume Next
Set elm = CreateObject("MSXML2.DOMDocument").createElement("base64")
With CreateObject("ADODB.Stream")
.Type = adTypeBinary
.Open
.LoadFromFile FilePath
elm.dataType = "bin.base64"
elm.nodeTypedValue = .Read(adReadAll)
ret = elm.text
.Close
End With
On Error GoTo 0
EncodeBase64FromFile = ret
End Function
Private Function DecodeBase64ToFile(ByVal Base64Str As String, ByVal FilePath As String) As Long
'読み込んで追加書きを刷る場合
'objStream.Position = objStream.size
'ファイルをBase64デコード
Dim elm As Object
Dim ret As Long
Const adTypeBinary = 1
Const adSaveCreateOverWrite = 2
ret = -1 '初期化
On Error Resume Next
Set elm = CreateObject("MSXML2.DOMDocument").createElement("base64")
elm.dataType = "bin.base64"
elm.text = Base64Str
'Dim i As Long, j As Long, k As Long
'Dim sz As Long
'Dim NTB() As Byte
'
'ReDim NTB(Len(Base64Str))
'
'For i = 1 To Len(Base64Str) Step 4
' elm.text = Mid(Base64Str, i, 4)
' For j = 0 To 2
' NTB(k) = elm.nodeTypedValue(j)
' k = k + 1
' Next
'Next
'1 2 4 8 16 32
'ReDim Preserve NTB(k)
With CreateObject("ADODB.Stream")
.Type = adTypeBinary
.Open
.Write elm.nodeTypedValue
.SaveToFile FilePath, adSaveCreateOverWrite
.Close
End With
If Err.Number <> 0 Then ret = 0
On Error GoTo 0
DecodeBase64ToFile = ret
End Function
'-----------------------------------------------------------------
' Sub B64ファイル分割(Read_File, Save_File, 分割行数 As Long)
'-----------------------------------------------------------------
' メモ:2022/12/07 今回はファイルから読み込んでファイル分割する方法は採用しなかった。
'-----------------------------------------------------------------
Sub B64ファイル分割(Read_File, Save_File, 分割行数 As Long)
'Dim Read_File As String, Save_File As String
'Dim Rec As Integer
Dim buf() As Variant
Dim i As Long, j As Long
'Read_File = "C:\sample.csv" '←適当に変えてください(フルパス)
'Save_File = "C:\sample_" '←適当に変えてください(連番の手前まで)
'Rec = 2 '分割するときのヘッダを除いたレコード数
ReDim buf(1 To 分割行数)
i = 1
Open Read_File For Input As #1
Dim 連番付与ファイル名 As String
Dim wPath As String
wPath = Left(Save_File, Len(Save_File) - Len(".txt"))
Do Until EOF(1) = True '終端に達するまでループ
連番付与ファイル名 = wPath & "_" & Right("00" & i, 3) & ".txt"
Open 連番付与ファイル名 For Output As #2
For j = 1 To 分割行数 'データ読み込み
Line Input #1, buf(j)
If EOF(1) = True Then Exit For
Next j
For j = 1 To 分割行数 'データ書き込み
Write #2, buf(j)
Next
For j = 1 To 分割行数 'データ初期化
buf(j) = ""
Next
Close #2
i = i + 1
Loop
Close #1
End Sub
'「テキスト」は72文字のあとvblfが1文字くっついている
Sub B64ファイル分割2(テキスト As String, Save_File As String, 分割行数 As Long)
On Error GoTo Catch
Dim buf As String
Dim i As Long, j As Long
Dim 連番付与ファイル名 As String
Dim wPath As String
wPath = Left(Save_File, Len(Save_File) - Len(".txt"))
j = 1
For i = 1 To Len(テキスト) Step 分割行数 * 73
Select Case True
Case i = 1
連番付与ファイル名 = wPath & "_001" & ".txt"
Open 連番付与ファイル名 For Output As #1
buf = Mid(テキスト, 1, 分割行数 * 73)
Case i Mod 分割行数 * 73 = 1
Close #1
j = j + 1
連番付与ファイル名 = wPath & "_" & Right("00" & j, 3) & ".txt"
Open 連番付与ファイル名 For Output As #1
buf = Mid(テキスト, i, 分割行数 * 73)
End Select
Print #1, buf;
Next
Close #1
Finally:
Exit Sub
Catch:
Debug.Print Err.Number, Err.Description
Stop
Resume
End Sub
'==================================================================================
' 機能概要: 特定のファイル(単一、複数)を指定したファイルに追加書きする
'----------------------------------------------------------------------------------
' 処理名称: Sub combineFiles1(sFolder As String,sFile as string, sCombineFileName As String)
' 引数説明: sFolder 任意のフォルダパス
' : sFile ファイル名(*,?で対象ファイル名を絞り込むことが可能)
' : sCombineFileName まとめる1ファイルのファイル名。パスは不要。
'==================================================================================
Sub combineFiles1(sFolder As String, sFile As String, sCombineFileName As String)
Dim FSO As Object
Dim oTS As TextStream '// TextStreamクラス
Dim oFolder As Folder '// フォルダ
Dim oFile As File '// ファイル
Dim iCharCode As Integer '// ファイル終端の文字コード
Dim oTsCombine As TextStream '// 書き込みファイル用のTextStreamクラス
Dim sRead As String '// 読み込みデータ
Dim sExtension As String '// 拡張子文字列
' フォルダがない場合
Set FSO = CreateObject("Scripting.FileSystemObject")
If FSO.FolderExists(sFolder) Then
Else
' 以降を処理せず抜ける
Exit Sub
End If
' 指定フォルダをFolderオブジェクトとして取得
Set oFolder = FSO.GetFolder(sFolder)
' まとめるファイルを新規ファイルとして作成
Set oTsCombine = FSO.CreateTextFile(oFolder.Path & "\" & sCombineFileName, True, False)
' カレントフォルダ内の全ファイルを1ファイルずつ取得
For Each oFile In oFolder.Files
' 作成するファイルの場合
If oFile.Name = sCombineFileName Then
' 読み込み対象からは除外する
GoTo Continue
End If
If oFile.Name Like sFile Then
Else
' 読み込み対象からは除外する
GoTo Continue
End If
' ファイルを開く
Set oTS = FSO.OpenTextFile(oFile.Path, ForReading)
' データを全て新規ファイルに書き込み
sRead = oTS.ReadAll
Call oTsCombine.Write(sRead)
' ファイルにデータがある場合
If oFile.size > 0 Then
' 終端文字の文字コードを取得(終端が全角文字の考慮でAscWを利用)
iCharCode = AscW(Right(sRead, 1))
Else
iCharCode = -1
End If
Call oTS.Close
' ファイル終端が改行コード(CR(13)、LF(10))ではない場合
If (iCharCode <> 13 And iCharCode <> 10) Then
' 改行コードを書き込む
Call oTsCombine.Write(vbCrLf)
End If
Continue:
Next
Call oTsCombine.Close
End Sub
'==================================================================================
' 機能概要: 複数のファイルを指定したファイルに追加書きする
'----------------------------------------------------------------------------------
' 処理名称: Sub combineFiles(sFolder As String, sCombineFileName As String)
' 引数説明: sFolder 任意のフォルダパス
' : sCombineFileName まとめる1ファイルのファイル名。パスは不要。
'==================================================================================
Sub combineFiles(sFolder As String, sCombineFileName As String)
Dim oFSO As Object
Dim oTS As TextStream '// TextStreamクラス
Dim oFolder As Folder '// フォルダ
Dim oFile As File '// ファイル
Dim iCharCode As Integer '// ファイル終端の文字コード
Dim oTsCombine As TextStream '// 書き込みファイル用のTextStreamクラス
Dim sRead As String '// 読み込みデータ
Dim sExtension As String '// 拡張子文字列
' フォルダがない場合
Set oFSO = CreateObject("Scripting.FileSystemObject")
If (oFSO.FolderExists(sFolder) = False) Then
' 以降を処理せず抜ける
Exit Sub
End If
' 指定フォルダをFolderオブジェクトとして取得
Set oFolder = oFSO.GetFolder(sFolder)
' まとめるファイルを新規ファイルとして作成
'fso.CreateTextFile(FileName [, Overwrite] [, Unicode])
'folder.CreateTextFile(FileName [, Overwrite] [, Unicode])
Set oTsCombine = oFSO.CreateTextFile(oFolder.Path & "\" & sCombineFileName, True, False)
' カレントフォルダ内の全ファイルを1ファイルずつ取得
For Each oFile In oFolder.Files
' 作成するファイルの場合
If oFile.Name = sCombineFileName Then
' 読み込み対象からは除外する
GoTo Continue
End If
' ファイルを開く
Set oTS = oFSO.OpenTextFile(oFile.Path, ForReading)
' データを全て新規ファイルに書き込み
sRead = oTS.ReadAll
Call oTsCombine.Write(sRead)
' ファイルにデータがある場合
If oFile.size > 0 Then
' 終端文字の文字コードを取得(終端が全角文字の考慮でAscWを利用)
iCharCode = AscW(Right(sRead, 1))
Else
iCharCode = -1
End If
Call oTS.Close
' ファイル終端が改行コード(CR(13)、LF(10))ではない場合
If (iCharCode <> 13 And iCharCode <> 10) Then
' 改行コードを書き込む
Call oTsCombine.Write(vbCrLf)
End If
Continue:
Next
Call oTsCombine.Close
End Sub