“对zip文件进行base64编码时,”没有足够的存储空间来完成此操作“

时间:2016-12-20 08:37:45

标签: vbscript base64 adodb xmldom

以下代码用于将zip文件转换为base64格式。

Dim inByteArray, base64Encoded,
Const TypeBinary = 1
inByteArray = readBytes("F:path/file.zip")
base64Encoded = encodeBase64(inByteArray)

Private Function readBytes(file)
    Dim inStream
    ' ADODB stream object used
    Set inStream = CreateObject("ADODB.Stream")
    ' open with no arguments makes the stream an empty container 
    inStream.Open
    inStream.Type = TypeBinary
    inStream.LoadFromFile(file)
    readBytes = inStream.Read()
End Function

Private Function encodeBase64(bytes)
    Dim DM, EL
    Set DM = CreateObject("Microsoft.XMLDOM")
    ' Create temporary node with Base64 data type
    Set EL = DM.CreateElement("tmp")
    EL.DataType = "bin.base64"
    ' Set bytes, get encoded String
    EL.NodeTypedValue = bytes
    encodeBase64 = EL.Text
End Function

我首先尝试使用大小为3MB的zip文件。它工作正常。但是,当我尝试使用大小为34 MB的zip文件时,它会说

  

没有足够的存储空间来完成此操作!

在第

encodeBase64 = EL.Text

有什么方法可以处理各种大小的zip文件,因为我的文件大小大多是30MB或更多。

1 个答案:

答案 0 :(得分:2)

已编辑 2017/01/10 - (原始答案保持在最底层)

已编辑 2017/01/10 - (再次) - 我的一些(不是全部)超时问题是由磁盘故障引起的。

通过拆分转换操作来处理输入数据的问题。现在代码已经被更改为以两种不同的方式处理缓冲:对于小文件(默认情况下配置为最多10MB的文件),内存流用于存储输出,但是对于大文件(大于{{1使用临时文件(参见代码后的注释)。

10MB

内存会出现问题吗?

是。可用内存仍然是一个限制。无论如何,我已经使用Option Explicit Dim buffer buffer = encodeFileBase64( "file.zip" ) WScript.StdOut.WriteLine( CStr(Len(buffer)) ) Private Function encodeFileBase64( file ) ' Declare ADODB used constants Const adTypeBinary = 1 Const adTypeText = 2 ' Declare FSO constants Const TEMP_FOLDER = 2 ' Initialize output encodeFileBase64 = "" ' Instantiate FileSystemObject Dim fso Set fso = WScript.CreateObject("Scripting.FileSystemObject") ' Check input file exists If Not fso.FileExists( file ) Then Exit Function End If ' Determine how we will handle data buffering. ' Use a temporary file for large files Dim useTemporaryFile useTemporaryFile = fso.GetFile( file ).Size > 10 * 1048576 ' Instantiate the B64 conversion component Dim b64 Set b64 = WScript.CreateObject("Microsoft.XMLDOM").CreateElement("tmp") b64.DataType = "bin.base64" Dim outputBuffer, outputBufferName If useTemporaryFile Then ' Create a temporary file to be used as a buffer outputBufferName = fso.BuildPath( _ fso.GetSpecialFolder( TEMP_FOLDER ), _ fso.GetTempName() _ ) Set outputBuffer = fso.CreateTextFile( outputBufferName, True ) Else ' Instantiate a text stream to be used as a buffer to avoid string ' concatenation operations that were generating out of memory problems Set outputBuffer = WScript.CreateObject("ADODB.Stream") With outputBuffer ' Two bytes per character, BOM prefixed buffer .Type = adTypeText .Charset = "Unicode" .Open End With End If ' Instantiate a binary stream object to read input file With WScript.CreateObject("ADODB.Stream") .Open .Type = adTypeBinary .LoadFromFile(file) ' Iterate over input file converting the file, converting each readed ' block to base64 and appending the converted text into the output buffer Dim inputBuffer Do inputBuffer = .Read(3145716) If IsNull( inputBuffer ) Then Exit Do b64.NodeTypedValue = inputBuffer If useTemporaryFile Then Call outputBuffer.Write( b64.Text ) Else Call outputBuffer.WriteText( b64.Text ) End If Loop ' Input file has been readed, close its associated stream Call .Close() End With ' It is time to retrieve the contents of the text output buffer into a ' string. If useTemporaryFile Then ' Close output file Call outputBuffer.Close() ' Read all the data from the buffer file encodeFileBase64 = fso.OpenTextFile( outputBufferName ).ReadAll() ' Remove temporary file Call fso.DeleteFile( outputBufferName ) Else ' So, as we already have a Unicode string inside the stream, we will ' convert it into binary and directly retrieve the data with the .Read() ' method. With outputBuffer ' Type conversion is only possible while at the start of the stream .Position = 0 ' Change stream type from text to binary .Type = adTypeBinary ' Skip BOM .Position = 2 ' Retrieve buffered data encodeFileBase64 = CStr(.Read()) ' Ensure we clear the stream contents .Position = 0 Call .SetEOS() ' All done, close the stream Call .Close() End With End If End Function 测试代码,运行为32位进程,文件为90MB,64位模式,500MB文件,无问题。

为什么有两种方法?

  • cscript.exe方法更快(所有操作都在没有字符串连接的内存中完成),但它需要更多内存,因为它在函数末尾有两个相同数据的副本:那里将是流中的一个副本和将返回的字符串中的一个

  • 临时文件方法较慢,因为缓冲的数据将写入磁盘,但由于只有一个数据副本,因此需要较少的内存。

用于确定我们是否使用临时文件的stream限制只是一个 pesimistic 配置,以防止32位模式出现问题。我已经在32位模式下处理了90MB文件而没有任何问题,但只是安全

为什么将10MB配置为stream并通过Unicode方法检索数据?

因为.Read() 。在内部,它会进行大量的字符串转换/检查(是的,建议在documentation中)使其在这种情况下无法使用。

下面是原始答案。它更简单,避免了转换中的内存问题,但对于大文件来说,这还不够。

拆分读/编码过程

stream.ReadText()

注意:

  • 不,它不是防弹的。您仍然受构造输出字符串所需空间的限制。对于大文件,您需要使用输出文件,编写部分结果,直到处理完所有输入。

  • 3145716只是54的最接近倍数(每个base64输出行的输入字节数)低于3145728(3MB)。