最終更新日:1999/08/10
BASE64のサンプルコード
Visual Basic V4で書いた,BASE64のENCODE/DECODEのサンプルプログラムです。
思いつきで,このコードを元にBASE64のEncode/Decodeを行うActiveX DLLを作ってみました。
詳細は,こちらをご覧下さい。
INDEX
利用方法
BASE64サンプルコード
注意書き
このサンプルコードの使い方
このサンプルコードは,BASE64のENCODE/DECODEを行います。
それぞれのコード変換を行うTOP関数は,base64encode/base64decodeになります。
受信したメールの変換を行う場合には,Base64Get/Base64Putのような前処理が必要になるでしょう。
- Sub base64encode(Bstr() As Byte, Blen As Long, B64str As String)
- 与えられたデータ列(Bstr())とデータ長(Blen)を,base64の規約に従って6bitのASCII文字列(B64str)に変換します。
- Sub base64decode(B64str As String, Bstr() As Byte, BCnt As Long)
- base64の規約に従って変換された6bitのASCII文字列(B64str)を復元して,データ列(Bstr())とデータ長(BCnt)を返します。
- Function Base64Get(Fname As String, inMailData As String, MailBody As Object) As Boolean
- 指定されたファイル名(Fname)を添付ファイル(Base64形式)として送信可能な形式にして,メールの本体のテキスト(inMailData)に付加してメール送信用のバッファオブジェクト(MailBody)のテキストプロパティに格納する。
メール送信用のバッファオブジェクトは,RichTextObjectとすること。
この関数の実行結果は,True(データ作成成功)/False(失敗)で返されます。
- Function Base64Put(Cmd1 As String, AnswerBody As Object, DirPos As String, Fname As String, Times As String, OptionDir As String) As Boolean
- メールとして受信した受信文字列(Cmd1)の添付ファイルを,指定したディレクトリ(OptionDir)に展開する。
展開された結果は,RichTextObjectのTextプロパティ(AnswerBody.Text)として得られます。
この関数の実行結果は,True(ファイル作成成功)/False(失敗)で返されます。
BASE64サンプルコード('lib_base64.bas')
ICPのテスト用に作成したBASE64用のENCODE/DECODEライブラリです。
Attribute VB_Name = "lib_base64"
'-----------------------------------------------------
'Base64 for MIME [1996/10/06]
' 1996 (c) by H.Yokomizo
'-----------------------------------------------------
'
'-----------------------------------------------------
'ここはコメントを外して使って下さい。
'Const MailVersion = "TestMailer Ver0.1 By H.Y."
'Const MIMEbound = "-@-@-@- " & MailVersion & " -@-@-@-"
'
'Public POPContentType As String ' MIME Contents Type
'Dim DQ As String '(") コード:Chr(34)
'
'Const InpSize = 32767 ' Input$ Input Max Size
'-----------------------------------------------------
Option Explicit
Sub base64encode(Bstr() As Byte, Blen As Long, B64str As String)
'----------------------------------------------------------------
' BASE64 ENCODER [1996/04/27]
' 8bit Binary -> 6bit ASCII (A-Z,a-z,0-9,+,/,[=])
' + Bstr : 8bit Binary Data
' + Blen : Bstr Data Length
' - B64str : Base64 ASCII Code Data
'----------------------------------------------------------------
'Base64の符号化は,3バイト(24bit)のバイナリデータを6bit単位の
'4つのデータに分割して,それを0-63(A-Z,a-z,0-9,+,/,[=])を表現
'する文字列に割り付けることで行う。
' 15 B1 0 15 B2 0 15 B3 0
' |----|----| |----|----| |----|----|Binary 8Bit * 3
' Case0 Case1 Case2 Case3 (Bmode)
' |------|-- ----|---- --|------|Base64 6Bit * 4
'データの最終端には"="を付けることがある。
'これはデータが24bitの途中で終わったことを示し,
'(8bit:2,16bit:1,24bit:0)の"="を付加する。
'変換されたデータは,1行最大76byteの文字列となる。
'Base64の符号化は,元データから約33%大きくなる。
'Base64についての詳細は,MIME,RFC1521,September 1993
'"http://www.cis.ohio-state.edu/htbin/rfc/rfc1521.html"
'を参照のこと。
'----------------------------------------------------------------
Dim Bmode As Integer 'バイナリデータの区切りモード(0-3)
Dim Cnt As Long 'バイナリデータのポインタ
Dim B1 As Byte '符号化するバイナリデータ
Dim RetVal As Integer
Dim Str1 As String
Dim m As Integer
Dim Pos As Integer
B64str = ""
Cnt = 0
Bmode = 0
Do Until Blen <= Cnt
' オペレーティング システムに制御を渡します。
If Fix(Cnt Mod 100) = 0 Then RetVal = DoEvents()
' Bitデータの整形
B1 = Bstr(Cnt)
Select Case Bmode
Case 0
B1 = (&HFC And B1) \ 4 '上位6Bit
Case 1
B1 = (&H3 And B1) * 16 '下位2Bit
Cnt = Cnt + 1 ' +
If Blen > Cnt Then
B1 = B1 + (&HF0 And Bstr(Cnt)) \ 16 '上位4Bit
End If
Case 2
B1 = (&HF And B1) * 4 '下位4Bit
Cnt = Cnt + 1 ' +
If Blen > Cnt Then
B1 = B1 + (&HC0 And Bstr(Cnt)) \ 64 '上位2Bit
End If
Case 3
B1 = &H3F And B1 '下位6Bit
Cnt = Cnt + 1
End Select
'Base64文字列作成(符号化)
B64str = B64str & BinToB64(B1)
Bmode = Bmode + 1
If Bmode > 3 Then Bmode = 0
Loop
'終端記号"="の付加
Select Case Bmode
Case 0 '何も付け加えない?
B64str = B64str
Case 1, 2 '"=="
B64str = B64str & "=="
Case 3 '"="
B64str = B64str & "="
End Select
'改行による76バイトの整形[1996/10/09]
Str1 = ""
m = 0
Do Until Len(B64str) <= m * 76
Pos = m * 76 + 1
If Len(B64str) - Pos > 76 Then
Str1 = Str1 & Mid$(B64str, Pos, 76) & vbCrLf
Else
Str1 = Str1 & Mid$(B64str, Pos, Len(B64str) - m * 76) & vbCrLf
End If
m = m + 1
Loop
B64str = Str1
Debug.Print "Base64 Last Encode Mode = "; Bmode
End Sub
Sub base64decode(B64str As String, Bstr() As Byte, BCnt As Long)
'----------------------------------------------------------------
' BASE64 DECODER [1996/04/27]
' 6bit ASCII -> 8bit Binary
' in B64str : Base64 ASCII Code Data
' out Bstr : 8bit Binary Data
' out BCnt : Bstr Data Length
'----------------------------------------------------------------
'Base64のASCII文字列(A-Z,a-z,0-9,+,/,[=])をバイナリデータに戻す。
'変換を行うために必要な配列を確保すること。
'変換文字列の後に,Base64のASCII文字を入れないこと
'???大量のデータを扱う場合には,適切なコード分割を行うこと???
'???分割する場合には,文字列の最後に"="を付加すること???
'----------------------------------------------------------------
Dim Bmode As Integer 'バイナリデータの区切りモード(0-3)
Dim ACnt As Long 'ASCIIデータのポインタ
Dim B1 As Byte '作成されたバイナリデータ
Dim RetVal As Integer
ACnt = 0
BCnt = 0
Bmode = 0
Do Until (Mid$(B64str, ACnt + 1, 1) = "=" Or Len(B64str) <= ACnt)
' オペレーティング システムに制御を渡します。
If Fix(ACnt Mod 100) = 0 Then RetVal = DoEvents()
'ASCIIデータの数値化(0-63)
B1 = B64ToBin(Mid$(B64str, ACnt + 1, 1))
If B1 >= 0 And B1 <= 63 Then
'Base64バイナリ変換
Select Case Bmode
Case 0
Bstr(BCnt) = B1 * 4 '上位6Bit
Case 1
Bstr(BCnt) = Bstr(BCnt) + (B1 \ 16) '下位2Bit
BCnt = BCnt + 1 ' +
Bstr(BCnt) = (&HF And B1) * 16 '上位4Bit
Case 2
Bstr(BCnt) = Bstr(BCnt) + (B1 \ 4) '下位4Bit
BCnt = BCnt + 1 ' +
Bstr(BCnt) = (&H3 And B1) * 64 '上位2Bit
Case 3
Bstr(BCnt) = Bstr(BCnt) + B1 '上位6Bit
BCnt = BCnt + 1
End Select
Bmode = Bmode + 1
If Bmode > 3 Then Bmode = 0
End If
ACnt = ACnt + 1
Loop
End Sub
Function Base64Get(Fname As String, inMailData As String, MailBody As Object) As Boolean
'-----------------------------------------------------
' 添付ファイルの作成
'指定されたファイルを添付ファイル(Base64形式)として
'送信可能な形式に格納する。
' + Fname : パス付きのファイル名
' + inMailData : テキスト部の格納データ
' - MailBody : 送信用データを格納するバッファオブジェクト
' (警告:RichTextObjectにすること)
' Ret : True/False
'-----------------------------------------------------
Dim Flen As Long
Dim Rsize As Long
Dim FNum As Integer
Dim Bstr() As Byte
Dim Astr As String
Dim B64str As String
Dim Ftitle As String
Dim n As Integer
On Error GoTo ChkError
'ファイルの存在確認
Flen = FileLen(Fname)
If Flen <= 0 Then
MailBody.Text = Fname & ":File Access Error" & vbCrLf
Debug.Print MailBody.Text
Base64Get = False
Exit Function
End If
ReDim Bstr(Flen)
FNum = FreeFile '未使用のファイル番号を取得します。
Open Fname For Binary Access Read As #FNum 'ファイルを開きます。
' Input$での制限(32,767Byte)付きREAD
If Flen >= InpSize Then
n = 0
Astr = ""
B64str = ""
Do
Bstr = InputB$(InpSize - 1, #FNum)
base64encode Bstr, InpSize - 1, Astr
B64str = B64str & Astr
n = n + 1
Loop Until (Flen - n * (InpSize - 1)) < InpSize
Bstr = InputB$(Flen - n * (InpSize - 1), #FNum)
base64encode Bstr, Flen - n * (InpSize - 1), Astr
B64str = B64str & Astr
Else
Bstr = InputB$(Flen, #FNum)
base64encode Bstr, Flen, B64str
End If
Close #FNum ' ファイルを閉じます。
' Set a FileTitle
Ftitle = Fname
Do Until Len(Ftitle) <= 0
Debug.Print Ftitle
n = InStr(Ftitle, "\")
If n = 0 Then Exit Do
Ftitle = Mid(Ftitle, n + 1)
Loop
' Set Plain Text Part
MailBody.Text = _
vbCrLf & "--" & MIMEbound & vbCrLf & _
"Content-Type: Text/Plain; Charset=iso-2022-jp" & vbCrLf & _
vbCrLf & vbCrLf & inMailData & vbCrLf & vbCrLf
' Set Multi Part of base64
MailBody.Text = MailBody.Text & "--" & MIMEbound & vbCrLf
MailBody.Text = MailBody.Text & "Content-Type: application/octet-stream; name=" & _
DQ & Ftitle & DQ & vbCrLf
MailBody.Text = MailBody.Text & "Content-Transfer-Encoding: base64" & vbCrLf & vbCrLf
MailBody.Text = MailBody.Text & B64str & vbCrLf & vbCrLf
MailBody.Text = MailBody.Text & "--" & MIMEbound & "--" & vbCrLf
Base64Get = True
Exit Function
ChkError:
MailBody.Text = Fname & ":" & Error$(Err) & vbCrLf
Debug.Print MailBody.Text
Base64Get = False
End Function
Function Base64Put(Cmd1 As String, _
AnswerBody As Object, _
DirPos As String, _
Fname As String, _
Times As String, _
OptionDir As String) As Boolean
'-----------------------------------------------------
'添付ファイルの展開[1996/10/06]
'指定されたディレクトリに添付ファイルを展開する
' + Cmd1 : 受信データ(2GBまで受信可)
' 32KB以上のデータ受信時のチェック要!
' - AnswerBody : Reply-Mail Body Object(RichTextObject)
' .Text = 実行結果メッセージ(Create or Error)
' - DirPos : Write Position
' - Fname : Base64 File Name
' - Times : Option Time
' + OptionDir : ""ならばメールのディレクトリを使う。それ以外
' なら,ここで指定したディレクトリを使う。
' Ret : True(ファイル作成成功)/False(失敗)
'-----------------------------------------------------
'注意書き
' 複数の添付ファイルには対応していません!。
' 一応AnswerBodyのTextプロパティがCmd1と同じでも大丈夫なはず!。
'-----------------------------------------------------
Dim Boundary As String
Dim Content1 As String
Dim Content2 As String
Dim Ftitle As String
Dim Str1 As String
Dim Str2 As String
Dim B64str As String
Dim Bstr() As Byte
Dim Blen As Long
Dim FNum As Integer
On Error GoTo ChkError
DirPos = ""
' MIME boundary Get
If InStr(POPContentType, "multipart/mixed;") > 0 Then
Str2 = SpritAString(POPContentType, DQ, Str1)
Str1 = SpritAString(Str2, DQ, Boundary)
If Len(Boundary) <= 0 Then
AnswerBody.Text = POPContentType & ";Error" & vbCrLf
Base64Put = False
Exit Function
End If
Else
AnswerBody.Text = POPContentType & ";Error" & vbCrLf
Base64Put = False
Exit Function
End If
' Content Analyze(Boundaryで2つに分ける)
Str1 = SpritAString(Cmd1, Boundary, Content1)
Content2 = SpritAString(Str1, Boundary, Content1)
' Command Check(Content1=メール本体部分)
'ディレクトリ設定オプション(OptionDir)の確認[1996/10/06]
If OptionDir <> "" Then
'指定があれば(<>"")指定されたディレクトリに展開
DirPos = OptionDir
Else
'指定がなければメール本体のディレクトリを利用
Do Until Len(Content1) <= 0
Content1 = SpritAString(Content1, vbCrLf, Str1)
If Len(Str1) > 0 Then
If InStr(Str1, "Content-") = 0 And InStr(Str1, "content-") = 0 Then
DirPos = Str1
TakeALine Content1, Times, Str1
Debug.Print "FILE PUT:" & DirPos & vbCrLf
Exit Do
End If
End If
Loop
End If
'ディレクトリの存在チェック
If DirExists(DirPos) = False Then
AnswerBody.Text = "Base64Put:Dir Error=" & DirPos & vbCrLf
Base64Put = False
Exit Function
End If
'対象ディレクトリに移動
ChDrive DirPos
ChDir DirPos
' Include File Check
Str1 = SpritAString(Content2, Boundary, Content1)
If Len(Str1) <= 0 Then
AnswerBody.Text = "Base64Put:Include Error" & vbCrLf
Base64Put = False
Exit Function
End If
' Base64 Content Check
Do Until Len(Content1) <= 0
Content1 = SpritAString(Content1, vbCrLf, Str1)
'ファイル名取得
If InStr(Str1, "application/octet-stream; name=") > 0 Then
Str1 = SpritAString(Str1, "name=", Str2)
Str2 = SpritAString(Str1, DQ, Fname)
If Str2 <> "" Then
Str1 = SpritAString(Str2, DQ, Fname)
End If
End If
' Base64変換Check
If InStr(Str1, "Base64") > 0 Or _
InStr(Str1, "BASE64") > 0 Or _
InStr(Str1, "base64") > 0 Then
Exit Do
End If
Loop
If Len(Content1) <= 0 Then
AnswerBody.Text = "Base64Put:Include Error" & vbCrLf
Base64Put = False
Exit Function
End If
'ファイル名設定
Ftitle = DirPos & "\" & Fname
Debug.Print "FILE = " & Ftitle & vbCrLf
'Base64変換文字列取得
Str1 = "?"
Do Until (Len(Str1) = 0)
If TakeALine(Content1, Str1, Content1) = False Then
AnswerBody.Text = "Base64Put:Base64 Strings Not Found." & vbCrLf
Base64Put = False
Exit Function
End If
Loop
Str1 = SpritAString(Content1, Boundary, B64str)
'Str1 = TakeAFirst(Content1, vbCrLf, B64str)
Debug.Print "Base64-Data:" & B64str & vbCrLf
If Len(B64str) <= 0 Then
AnswerBody.Text = "Base64Put:Base64 Strings Not Found!" & vbCrLf
Base64Put = False
Exit Function
End If
'Base64変換
ReDim Bstr(Len(B64str) * 2) As Byte
base64decode B64str, Bstr(), Blen
Debug.Print "Decode Size ="; Blen
ReDim Preserve Bstr(Blen - 1) As Byte
' Include File Write
FNum = FreeFile ' 未使用のファイル番号を取得します。
'Open Ftitle For Binary Access Write As #Fnum Len = Blen '---Lenの最大は32,767
Open Ftitle For Binary Access Write As #FNum 'BinaryAccessのためLenを削除[96/10/02]
Put #FNum, 1, Bstr
Close #FNum ' ファイルを閉じます。
AnswerBody.Text = Ftitle & ":Created" & vbCrLf
Base64Put = True
Exit Function
ChkError:
AnswerBody.Text = "Base64Put(" & DirPos & "):Error" & vbCrLf
Debug.Print AnswerBody.Text
Base64Put = False
End Function
Function BinToB64(B1 As Byte) As String
'----------------------------------------------------------------
' バイナリデータをBase64のASCIIデータへ変換する
' in B1 : Base64 Binary (0-63)
' return : Base64 Ascii Code
'----------------------------------------------------------------
Dim A1 As String
If B1 <= 25 Then A1 = Chr(Asc("A") + B1)
If B1 > 25 And B1 <= 51 Then A1 = Chr(Asc("a") + B1 - 26)
If B1 > 51 And B1 <= 61 Then A1 = Chr(Asc("0") + B1 - 52)
If B1 = 62 Then A1 = "+"
If B1 = 63 Then A1 = "/"
BinToB64 = A1
'Debug.Print B1; A1
End Function
Function B64ToBin(A1 As String) As Byte
'----------------------------------------------------------------
' Base64のASCIIデータをバイナリデータに変換する
' in A1 : Base64 Ascii Char(1Byte)
' return : Base64 Binary (0-63)
'文字列長が1でないときは255を返す。
'Base64対象文字以外は255を返す。
'----------------------------------------------------------------
Dim B1 As Byte
If Len(A1) <> 1 Then
B64ToBin = 255
Exit Function
End If
B1 = 255
If Asc(A1) >= Asc("A") And Asc(A1) <= Asc("Z") Then B1 = Asc(A1) - Asc("A") + 0
If Asc(A1) >= Asc("a") And Asc(A1) <= Asc("z") Then B1 = Asc(A1) - Asc("a") + 26
If Asc(A1) >= Asc("0") And Asc(A1) <= Asc("9") Then B1 = Asc(A1) - Asc("0") + 52
If A1 = "+" Then B1 = 62
If A1 = "/" Then B1 = 63
B64ToBin = B1
End Function
Function TakeALine(Str1 As String, Str2 As String, Str3 As String) As Boolean
'-------------------------------------------------
'文字列を改行コードで分けて返す。
' Str1 : 元の文字列
' Str2 : 最初の行(CRLFは含まない)
' Str3 : 残りの行
' 戻り値 : False=改行コード無,True=分割成功
'改行のみの場合,Str2は長さ0の文字列となる。
'-------------------------------------------------
'Calling:
' Ret = TakeALine(Text, First, Others)
' or
' Ret = TakeALine(ALL,First,ALL)
'-------------------------------------------------
Dim Pos As Integer
Pos = InStr(Str1, vbCrLf)
If Pos > 0 Then
TakeALine = True
Str2 = Left(Str1, Pos - 1)
Str3 = Mid(Str1, Pos + Len(vbCrLf), Len(Str1) - Pos - Len(vbCrLf) + 1)
Else
TakeALine = False
Str2 = ""
Str3 = ""
End If
End Function
Function SpritAString(ByVal Str1 As String, Str2 As String, Str3 As String) As String
'----------------------------------------------------------
'文字列を特定文字(vbCrLf,",",etc)で分けて返す。
' Str1 : 元の文字列
' Str2 : 特定文字(Ex,TAB,","," ",etc)
' Str3 : 最初の文字列(特定文字は含まない)
' 戻り値 : 残りの文字列(特定文字列が無いときは""を返す)
'----------------------------------------------------------
' Calling:
' Others = SpritAString(Source,SpritString,First)
'----------------------------------------------------------
Dim Pos As Integer
Pos = InStr(Str1, Str2)
If Pos > 0 Then
' 特定文字列が含まれていると,その文字列の前と後に文字列を分けて返す
Str3 = Left(Str1, Pos - 1)
SpritAString = Mid(Str1, Pos + Len(Str2), Len(Str1) - Pos - Len(Str2) + 1)
Else
' 特定文字列が含まれない時は元の文字列を最初の文字列としてそのまま返す
Str3 = Str1
SpritAString = ""
End If
End Function
注意書き
VBを使う上での一般常識の無さはご勘弁下さい(なにせ日曜プログラマなもので)。
苦情も受け付けていますが,できれば「こうしたら上達するよっ」といった点を指摘していただけるとありがたいです。
バグリポートについては,この場に掲載させていただくことをご了承下さい。
サンプルコードへの反映については,将来的に対応するかもしれません,ということにさせて下さい。
とにかくVB4で大容量の文字処理を行うことは実行時間上無理があることを実感したので,特にバージョンアップ等は考えていません。
このコードを用いて,WinBiffおよび電信8号との添付ファイルの送受信テストは,特に問題ありませんでした。
但し以下の項目については,若干から非常に...の間で怪しい場合があります。
- 32KBを越える添付ファイルのENCODE/DECODE
- ASCII"8.3"形式以外を添付ファイル名とした場合
- とにかく異常処理全般
サンプルコードの関数のコメント内に"+","-"とあるのは,引数がその関数に対してIN("+")あるいはOUT("-")のデータを持つことを示します。
(もちろん関数の戻り値(Return)はOUT("-")になります)
本来ならByRef,ByVal等を付けた方が良かったと思いますが,昔からのクセで使ってますのでご了解下さい。
戻る
技術的なメモのINDEXへ
(c) 1996 H.Yokomizo All Right Reserved.