I'm getting the aforementioned run-time error 'Out of string space' when decoding a base 64 string into a PDF document. This obviously only happens when decoding larger documents (164 page PDF in this case) but is there any way to adjust the method to cope with larger base 64 strings?
The code used is below -
Private Sub Apply_Click()
Dim strPath As String
Dim b64test As String
User = Split(Application.username, " ")
b64 = FinalResult
strPath = "C:\Users\" & User(1) & "\Downloads" & "\" & ConsignmentNumber & " Invoice.pdf"
Open strPath For Binary As #1
Put #1, 1, DecodeBase64(b64)
Close #1
End Sub
Private Function DecodeBase64(ByVal strData As String) As Byte()
Dim objXML As Object
Dim objNode As Object
Set objXML = CreateObject("MSXML2.DOMDocument")
Set objNode = objXML.createElement("b64")
objNode.DataType = "bin.base64"
objNode.Text = strData
DecodeBase64 = objNode.nodeTypedValue
Set objNode = Nothing
Set objXML = Nothing
End Function
This seems to work for me. You can base64 encode/decode in chunks, as long as you use a multiple of 3 bytes going in, and 4 characters the other way.
I was able to repeatedly encode/decode a >200MB file with no errors, but it's not very fast (decode is slower than encode).
Option Explicit
'Show a round-trip encode/decode for a binary file to base64 .dat file and back...
'Add a VB Project reference to the ADODB object library.
Sub Base64RoundTrip()
Const BYTESCHUNK As Long = 1800000
Const B64CHUNK As Long = 2048000
Dim bytes() As Byte, inBytes, b64 As String, diff As Long, inFile As String, outFile As String
Dim folder As String, streamIn As ADODB.stream, streamOut As ADODB.stream, datFile As String
folder = "C:\Temp\B64\" 'all files are here
datFile = folder & "b64.dat" 'for the Base64-encoded content
inFile = folder & "test.pdf" 'the file being encoded
outFile = folder & "test 2.pdf" 'round-trip output file
'### Encode process ###
KillIfExists datFile
Set streamIn = New ADODB.stream
With streamIn
.Type = adTypeBinary
.Open
.LoadFromFile inFile
End With
Set streamOut = New ADODB.stream
With streamOut
.Open
.Type = adTypeText
.Charset = "utf-8"
End With
Do
'Read the file to be encoded in chunks, and append the
' base 64-encoded text to the .dat file
inBytes = streamIn.Read(BYTESCHUNK)
If IsNull(inBytes) Then Exit Do
streamOut.WriteText EncodeBase64(inBytes)
Loop
streamIn.Close
streamOut.SaveToFile datFile
streamOut.Close
Debug.Print "encoded"
'done creating B64 file
'### Decode process - read in the B64 file and decode in chunks ###
Set streamIn = New ADODB.stream
With streamIn
.Open
.Type = adTypeText
.Charset = "utf-8"
.LoadFromFile datFile
End With
Set streamOut = New ADODB.stream
With streamOut
.Open
.Type = adTypeBinary
End With
KillIfExists outFile
Do
If Not streamIn.EOS Then
b64 = b64 & streamIn.ReadText(B64CHUNK * 1.05) 'padding for vbLf replacement
b64 = Replace(b64, vbLf, "") 'remove any newlines
Else
Debug.Print "EOS" 'no more file content
End If
bytes = DecodeBase64(Left(b64, B64CHUNK)) 'convert first B64CHUNK characters
streamOut.Write bytes 'write to output stream
diff = Len(b64) - B64CHUNK
b64 = Right(b64, IIf(diff < 0, 0, diff)) 'remove the part we just encoded
If Len(b64) = 0 Then Exit Do 'out of text to encode...
Loop
streamIn.Close
streamOut.SaveToFile outFile
streamOut.Close
Debug.Print "decoded"
End Sub
Function DecodeBase64(ByVal strData As String) As Byte()
With CreateObject("MSXML2.DOMDocument").createElement("b64")
.DataType = "bin.base64"
.Text = strData
DecodeBase64 = .nodeTypedValue
End With
End Function
Function EncodeBase64(bytes) As String
With CreateObject("MSXML2.DOMDocument").createElement("b64")
.DataType = "bin.base64"
.nodeTypedValue = bytes
EncodeBase64 = .Text
End With
End Function
'kill a file: ignore error if it doesn't exist
Sub KillIfExists(fPath As String)
On Error Resume Next
Kill fPath
On Error GoTo 0
End Sub