Тема: VBScript & CMD: разделение файла на фрагменты и их последующее слияние
По мотивам темы LangMF 7.7:Разбиение файла на фрагменты.
Я решил попробовать, насколько возможно проделать аналогичную операцию, заменив использование LangMF функционалом ADODB.Stream. Практическое применение данный скрипт в настоящее время вряд ли будет иметь, но демонстрирует ещё один пример работы с бинарными данными посредством ADODB.Stream.
Замечания:
* скрипт предназначен для исполнения под хостом cscript.exe;
* Microsoft в статье LoadFromFile Method в ADO 2.8 SDK пишет:
Because 2 bytes may be added to the beginning of the stream for encoding, the size of the stream may not exactly match the size of the file from which it was loaded.
Надеюсь, что эти байты, даже если и появляются, то также исчезают при записи потока методом SaveToFile.
Split.vbs
Option Explicit
Const adTypeBinary = 1
Const adTypeText = 2
Const adModeRead = 1
Const adModeWrite = 2
Const adModeReadWrite = 3
Const adSaveCreateNotExist = 1
Const adSaveCreateOverWrite = 2
Dim objNamedArgs
Dim objWshShell
Dim objFSO
Dim objTS
Dim objStreamSource
Dim objStreamDest
Dim strSourceFile
Dim strDestFolder
Dim lngPartSize
Dim boolNoAskReWrite
Dim boolCreateCMD
Dim intPartNumber
Dim boolDone
Set objNamedArgs = WScript.Arguments.Named
Set objWshShell = WScript.CreateObject("WScript.Shell")
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
Set objStreamSource = WScript.CreateObject("ADODB.Stream")
Set objStreamDest = WScript.CreateObject("ADODB.Stream")
If objNamedArgs.Exists("Help") Or objNamedArgs.Exists("?") Then
ShowUsage
QuitInfo "", 0, 0
End If
If objNamedArgs.Exists("SourceFile") Then
strSourceFile = objNamedArgs.Item("SourceFile")
If Not objFSO.FileExists(strSourceFile) Then
QuitInfo "Исходный файл " & strSourceFile & " не найден", 2, 0
End If
Else
ShowUsage
QuitInfo "Не указан обязательный параметр /SourceFile", 1, 0
End If
If objNamedArgs.Exists("DestFolder") Then
strDestFolder = objNamedArgs.Item("DestFolder")
Else
strDestFolder = objWshShell.CurrentDirectory
End If
If objNamedArgs.Exists("PartSize") Then
lngPartSize = Clng(objNamedArgs.Item("PartSize"))
Else
lngPartSize = 1457664
End If
If objNamedArgs.Exists("Y") Then
boolNoAskReWrite = True
Else
boolNoAskReWrite = False
End If
If objNamedArgs.Exists("Cmd") Then
boolCreateCMD = True
Else
boolCreateCMD = False
End If
With WScript.StdOut
.WriteLine ""
.WriteLine "Разделение файла на части"
.WriteLine ""
.WriteLine "Исходный файл : " & strSourceFile
.WriteLine "Папка назначения для набора : " & strDestFolder
.WriteLine "Размер отдельной части набора : " & FormatNumber(lngPartSize, 0) & " байт"
.WriteLine "Перезаписывать существующие части набора : " & IIf(boolNoAskReWrite, "Да", "Спросить")
.WriteLine "Создавать командный файл для слияния : " & IIf(boolCreateCMD, "Да", "Нет")
.WriteLine ""
End With
intPartNumber = 1
boolDone = False
With objStreamDest
.Mode = adModeReadWrite
.Type = adTypeBinary
.Open
End With
With objStreamSource
.Mode = adModeReadWrite
.Type = adTypeBinary
.Open
.LoadFromFile strSourceFile
Do
WritePart objStreamSource
intPartNumber = intPartNumber + 1
Loop Until .EOS Or boolDone
.Close
End With
With objStreamDest
.Close
End With
If boolCreateCMD And Not boolDone Then
Set objTS = objFSO.CreateTextFile(objfso.BuildPath(strDestFolder, "Join.cmd"), True, False)
objTS.Write CmdText()
objTS.Close
Set objTS = Nothing
End If
Set objStreamDest = Nothing
Set objStreamSource = Nothing
Set objFSO = Nothing
Set objWshShell = Nothing
Set objNamedArgs = Nothing
WScript.Quit 0
'=============================================================================
'=============================================================================
Sub WritePart(ByRef objStream)
Dim strPartNumber
Dim strDestPartFile
strPartNumber = Right("0000" & CStr(intPartNumber), Len("0000"))
strDestPartFile = objFSO.BuildPath(strDestFolder, _
objFSO.GetFileName(strSourceFile) & ".part" & strPartNumber & ".bin")
WScript.StdOut.WriteLine "Обрабатывается часть #" & strPartNumber & ": " & strDestPartFile
With objStreamDest
.Write objStream.Read(lngPartSize)
If objFSO.FileExists(strDestPartFile) Then
If boolNoAskReWrite Then
.SaveToFile strDestPartFile, adSaveCreateOverWrite
Else
If MsgBox("Файл " & strDestPartFile & " существует. Перезаписать?", _
vbOKCancel + vbQuestion + vbDefaultButton2) = vbOK Then
.SaveToFile strDestPartFile, adSaveCreateOverWrite
Else
WScript.StdErr.WriteLine "Обработка была прервана по желанию пользователя"
boolDone = True
End If
End If
Else
.SaveToFile strDestPartFile, adSaveCreateNotExist
End If
.Position = 0
.SetEOS
End With
End Sub
'=============================================================================
'=============================================================================
Sub QuitInfo(strInfo, intErrorlevel, intPause)
With WScript
If Len(strInfo) <> 0 Then
If intErrorlevel = 0 Then
.StdOut.WriteLine strInfo
Else
.StdErr.WriteLine strInfo
End If
End If
If intPause <> 0 Then
.Sleep intPause * 1000
End If
.Quit intErrorlevel
End With
End Sub
'=============================================================================
'=============================================================================
Sub ShowUsage()
WScript.StdOut.WriteLine _
"Использование:" & vbCrLf & _
" " & WScript.ScriptName & " /Help | /?" & vbCrLf & _
" " & WScript.ScriptName & " /SourceFile:<FileName> [/DestFolder:<Path>] [/PartSize:<Size>] [/Y] [/Cmd]" & vbCrLf & _
"" & vbCrLf & _
"Описание :" & vbCrLf & _
" Скрипт " & WScript.ScriptName & " позволяет разделить файл на несколько частей" & vbCrLf & _
" (в данной версии скрипта — не более 9999 частей) заданного размера." & vbCrLf & _
" Эти части в последующем можно слить в исходный файл с помощью команды" & vbCrLf & _
" copy или пакетного файла, создание которого можно задать при разделении." & vbCrLf & _
"" & vbCrLf & _
"Параметры :" & vbCrLf & _
"/Help или /? : Эта справка." & vbCrLf & _
"" & vbCrLf & _
"/SourceFile : Имя исходного файла для разбиения на части." & vbCrLf & _
" Файлы отдельных частей получат имена вида <name.ext.part###.bin>, где:" & vbCrLf & _
" name.ext — имя.расширение исходного файла;" & vbCrLf & _
" ### — номер отдельной части исходного файла." & vbCrLf & _
" Впоследствии они могут быть склеены в исходный файл <name.ext> командой" & vbCrLf & _
" copy /b name.ext.part001.bin+...+name.ext.partNNN.bin name.ext" & vbCrLf & _
" См. также параметр /Cmd ниже." & vbCrLf & _
"" & vbCrLf & _
"/DestFolder : Папка, куда будут помещены отдельные части исходного файла." & vbCrLf & _
" Если не указана, будет использована текущая папка." & vbCrLf & _
"" & vbCrLf & _
"/PartSize : Размер отдельной части." & vbCrLf & _
" Если не указан, подразумевается размер стандартной 3.5"" дискеты" & vbCrLf & _
" — 1.44 Мб (1457664 байт)." & vbCrLf & _
"" & vbCrLf & _
"/Y : Перезаписывать существующие файлы набора без подтверждения." & vbCrLf & _
" Если параметр /Y не указан, будет выдан запрос на перезапись" & vbCrLf & _
" существующего файла." & vbCrLf & _
"" & vbCrLf & _
"/Cmd : Создать пакетный файл для слияния отдельных частей." & vbCrLf & _
" В той же папке, куда будут помещены отдельные части файла, будет создан" & vbCrLf & _
" пакетный файл Join.cmd. Чтобы воссоздать исходный файл из набора" & vbCrLf & _
" отдельных частей, запустите пакетный файл Join.cmd, задав ему" & vbCrLf & _
" в качестве параметра имя файла первой части (*.part0001.bin) набора." & vbCrLf & _
" Если параметр /Cmd не указан, пакетный файл не создаётся." & vbCrLf & _
"" & vbCrLf & _
"Примеры :" & vbCrLf & _
" " & WScript.ScriptName & " /SourceFile:""c:\My Downloads\K-Lite Codec Pack\klcodec400f.exe""" & vbCrLf & _
" " & WScript.ScriptName & " /SourceFile:""c:\xpcd\i386\driver.cab"" /PartSize:5000000" & vbCrLf & _
" " & WScript.ScriptName & " /SourceFile:driver.cab /DestFolder:""c:\temp"" /Y /Cmd" & vbCrLf & _
""
End Sub
'=============================================================================
'=============================================================================
Function CmdText()
Dim i
Dim arrStrings
CmdText = _
"@echo off " & vbCrLf & _
" " & vbCrLf & _
"setlocal enableextensions enabledelayedexpansion " & vbCrLf & _
" " & vbCrLf & _
"if [%1]==[] ( " & vbCrLf & _
" call :Using %~nx0 " & vbCrLf & _
" exit /b 1 " & vbCrLf & _
") " & vbCrLf & _
" " & vbCrLf & _
"set tsf=%~1 " & vbCrLf & _
"set sf=%tsf:.part0001.bin=% " & vbCrLf & _
" " & vbCrLf & _
"if [""%tsf%""]==[""%sf%""] ( " & vbCrLf & _
" call :NotFirstPart %tsf% %~nx0 " & vbCrLf & _
" exit /b 2 " & vbCrLf & _
") " & vbCrLf & _
" " & vbCrLf & _
"if not exist ""%tsf%"" ( " & vbCrLf & _
" call :NotExistFirstPart %tsf% " & vbCrLf & _
" exit /b 3 " & vbCrLf & _
") " & vbCrLf & _
" " & vbCrLf & _
"echo. " & vbCrLf & _
"echo Восстановление исходного файла %sf% из частей " & vbCrLf & _
"echo. " & vbCrLf & _
" " & vbCrLf & _
"copy /y nul ""%sf%"" > nul " & vbCrLf & _
" " & vbCrLf & _
"for /l %%i in (1,1,9999) do ( " & vbCrLf & _
" set number=0000%%i " & vbCrLf & _
" set pf=%sf%.part!number:~-4!.bin " & vbCrLf & _
" " & vbCrLf & _
" if exist ""!pf!"" ( " & vbCrLf & _
" echo Слияние !pf!... " & vbCrLf & _
" copy /b /y ""%sf%""+""!pf!"" ""%sf%"" > nul " & vbCrLf & _
" ) else ( " & vbCrLf & _
" echo. " & vbCrLf & _
" echo Процесс слияния завершён " & vbCrLf & _
" goto :eof " & vbCrLf & _
" ) " & vbCrLf & _
") " & vbCrLf & _
" " & vbCrLf & _
"endlocal " & vbCrLf & _
" " & vbCrLf & _
"exit /b 0 " & vbCrLf & _
"::============================================================================" & vbCrLf & _
" " & vbCrLf & _
"::============================================================================" & vbCrLf & _
":Using " & vbCrLf & _
" echo. " & vbCrLf & _
" echo Использование: %1 ^<имя файла первой части набора^> " & vbCrLf & _
" echo. " & vbCrLf & _
" echo Пример : %1 driver.cab.part0001.bin " & vbCrLf & _
" echo. " & vbCrLf & _
" echo При отсутствии хотя бы одной из отдельных частей набора " & vbCrLf & _
" echo обработка завершается, и полученный слитый файл не будет " & vbCrLf & _
" echo совпадать с исходным. " & vbCrLf & _
" goto :eof " & vbCrLf & _
"::============================================================================" & vbCrLf & _
" " & vbCrLf & _
"::============================================================================" & vbCrLf & _
":NotFirstPart " & vbCrLf & _
" echo. " & vbCrLf & _
" echo Файл %1 не является первой частью набора " & vbCrLf & _
" echo. " & vbCrLf & _
" call :Using %2 " & vbCrLf & _
" goto :eof " & vbCrLf & _
"::============================================================================" & vbCrLf & _
" " & vbCrLf & _
"::============================================================================" & vbCrLf & _
":NotExistFirstPart " & vbCrLf & _
" echo. " & vbCrLf & _
" echo Файл %1 не найден " & vbCrLf & _
" echo. " & vbCrLf & _
" goto :eof " & vbCrLf & _
"::============================================================================" & vbCrLf
arrStrings = Split(CmdText, vbCrLf)
For i = LBound(arrStrings) To UBound(arrStrings)
arrStrings(i) = RTrim(arrStrings(i))
Next
CmdText = StrConvert(Join(arrStrings, vbCrLf), "cp866", "Windows-1251")
End Function
'=============================================================================
'=============================================================================
Function StrConvert(strText, strSourceCharset, strDestCharset)
Const adTypeText = 2
Const adModeReadWrite = 3
Dim objStream
Set objStream = WScript.CreateObject("ADODB.Stream")
With objStream
.Type = adTypeText
.Mode = adModeReadWrite
.Open
.Charset = strSourceCharset
.WriteText strText
.Position = 0
.Charset = strDestCharset
strConvert = .ReadText
End With
Set objStream = Nothing
End Function
'=============================================================================
'=============================================================================
Function IIf(strIF, strThen, strElse)
If Eval(strIF) Then
If UCase(TypeName(strThen)) = "STRING" Then
IIf = strThen
Else
IIf = Eval(strThen)
End If
Else
If UCase(TypeName(strElse)) = "STRING" Then
IIf = strElse
Else
IIf = Eval(strElse)
End If
End If
End Function
'=============================================================================
Время сборки исходного файла из полученных отдельных частей с помощью сгенерированного пакетного файла, из-за особенностей работы команды copy и неоптимальности применённого алгоритма, растёт в геометрической прогрессии с размером исходного файла и количеством отдельных частей и может занимать очень значительное время:
Файл размером 732 439 773 был разбит [скриптом — alexii] на 3663 части примерно за 10 минут.
Сборка - сильно дольше, начал тормозить где-то на 1200-м файле, через полтора часа я вырубил (собралось чуть меньше, чем наполовину), Celeron(R) D CPU 3.06GHz, 500 Мб ОЗУ, WinXP SP3.