VBScriptで挑む大容量ファイル転送:Rangeヘッダーによる「レジリエンス」の実装術
業務自動化の現場で、ファイルダウンロード処理が「タイムアウトで落ちる」「途中で切れる」という問題に直面したことはないだろうか?
特にVBScript/WSH環境では、現代の非同期ライブラリのような高機能なリトライ機構は標準搭載されていない。だが、諦める必要はない。HTTPプロトコルの根幹である「Rangeヘッダー」をMSXML2.ServerXMLHTTPで制御すれば、中断された地点から再開する「レジリエンス(回復力)」の高い転送ロジックを自前で実装できる。
今回は、情弱なエラーハンドリングを卒業し、プロダクション環境で耐えうる堅牢なダウンロードロジックの極意を伝授する。
—
1. なぜ「全件一括ダウンロード」が失敗するのか
VBScriptでよく見かける `ADODB.Stream` と `ServerXMLHTTP` を使った単純なダウンロードコード。あれは小規模なファイルには適しているが、数GB規模や不安定なネットワーク環境では自殺行為だ。
- メモリ枯渇: `responseBody` を一度にメモリへロードすると、オブジェクトのライフサイクル管理が追いつかず、WSHプロセスが肥大化してクラッシュする。
- タイムアウト: ネットワークの瞬断は必ず起きる。そこで「最初からやり直し」にする設計は、帯域と時間の無駄だ。
解決策は「追記モード」と「Range要求」の組み合わせにある。
—
2. 実装の要諦:Rangeリクエストの設計思想
HTTPの `Range: bytes=開始位置-` ヘッダーを送信することで、サーバーから「指定したオフセット以降」のデータだけを要求できる。
1. 既存ファイルサイズの確認: `Scripting.FileSystemObject` でローカルのファイルサイズを取得する。
2. Rangeヘッダーの指定: `xhr.setRequestHeader “Range”, “bytes=” & currentSize & “-“` をセット。
3. ADODB.Streamの追記モード: `Position` をファイル末尾に移動してから書き込む。
—
3. 【プロダクションコード】中断対応ダウンロード・ルーチン
以下のコードは、単に動くだけでなく、ファイルシステムのステータスを確認して「何バイト目から再開すべきか」を自動判断する設計となっている。
‘ ダウンロードを再開可能にする堅牢なファンクション
Function DownloadFileWithResume(url, savePath)
Dim xhr, stream, fso, currentSize
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set stream = CreateObject(“ADODB.Stream”)
‘ 既にファイルが存在すればサイズを取得(これが再開位置になる)
currentSize = 0
If fso.FileExists(savePath) Then
currentSize = fso.GetFile(savePath).Size
End If
Set xhr = CreateObject(“MSXML2.ServerXMLHTTP.6.0”)
xhr.Open “GET”, url, False
‘ 中断からの再開のためにRangeヘッダーを付与
If currentSize > 0 Then
xhr.setRequestHeader “Range”, “bytes=” & currentSize & “-”
End If
xhr.Send
‘ ステータス206(Partial Content)または200(新規)を確認
If xhr.Status = 200 Or xhr.Status = 206 Then
stream.Type = 1 ‘ adTypeBinary
stream.Open
‘ ファイル末尾へ移動(追記モード)
If currentSize > 0 Then
stream.LoadFromFile savePath
stream.Position = stream.Size
End If
‘ 取得したデータをバイナリで書き込み
stream.Write xhr.responseBody
stream.SaveToFile savePath, 2 ‘ adSaveCreateOverWrite
stream.Close
DownloadFileWithResume = True
Else
DownloadFileWithResume = False
End If
Set stream = Nothing
Set xhr = Nothing
End Function
—
4. エンジニアとして押さえておくべき「罠」
このロジックを実装する際、以下の3点だけは絶対に忘れてはならない。
- サーバー側の対応: そもそも対象サーバーが `Accept-Ranges: bytes` を返さない場合、この手法は使えない。通信の冒頭でヘッダーを確認するデバッグルーチンを挟むのがプロの仕事だ。
- ファイルロックの競合: VBScriptはシングルスレッドだが、ウイルス対策ソフトがダウンロード中のファイルをスキャンしようとすると、`SaveToFile` で「アクセス拒否」が発生する。書き込み直前のリトライ処理(ループ)を入れておくのが、現場で泣かないための鉄則だ。
- ADODB.Streamの限界: `responseBody` を介する方法は、依然としてメモリ上に全データを展開する。数GB単位の超巨大ファイルの場合は、バイナリをチャンク(細切れ)で読み込む `ADODB.Stream` のストリーミング転送へ移行する必要がある。今回は「まずは再開機能」という第一歩として最適化している。
結びに:自動化の本質は「失敗を許容する」こと
自動化ツールの価値は「完璧に動くこと」ではなく、「失敗したときに、いかに少ないコストで復旧できるか」にある。
今回紹介したRangeリクエストによるダウンロード処理は、ネットワークの不安定さを「前提」とした設計だ。皆さんの業務環境にあるレガシーなインフラに対しても、このアプローチを適用すれば、安定感のある強力なツールが必ず作れるはずだ。
コードをコピペして終わりにするな。なぜそのヘッダーが必要なのか、なぜストリームを追記モードにするのか。その「意図」を理解した者だけが、真の自動化エンジニアと呼べる。健闘を祈る。
