2023/08/19

HTTP でファイルをダウンロード(いにしえの呪文 - R5 以前)

前回、HTTP でファイルをダウンロード(いにしえの呪文)で、Windows API の一つである、Windows インターネット(WinINet API)を使用する方法をご紹介しました。

そのソースコードでは、API からデータを取得するバッファエリアの確保に Byte 型の配列を使用していました。

   Dim abyRecvData() As Byte


ただ、LotusScript で Byte 型がサポートされたのは Notes 6 以降です。R4.6 や R5.0 では実行できません。そのような環境で実行する方法をご紹介します。

なお、今回の記事は R4.6 利用当時のソースコードをもとに記載しています(R4.5 以前は全く未検証)。テストは 11.0.1 と 12.0.2 で実施しており、当時のバージョンでの再テストは実施できていません。

また、この記事は、API が欲する型が利用できない場合の対策の一例として紹介することは主な目的です。R5 や R4.6 の復権や活用を目的とするものではありません。

あらかじめご了承ください。


対策の例

R4.6 や R5.0 の LotusScript でも使用できる Integer 型の変数を使用する場合を例に記載します。

バッファ配列の型を Integer 型に変更しています。型が変わったので変数名のプリフィックス(接頭文字)を変えています。

LotusScript の場合、Integer 型は 2 バイトです。それを係数として保持させる変数が iByte です。また、バッファ配列の最大インデックス番号を保持する iIdxMax 変数も新設しています。

サンプルコードは次の通りです。変更箇所を赤字で示します。

Public Function HttpDownload(Byval vsUrl As String, Byval vsFileName As String) As Long
   Dim aiRecvData() As Integer
      ・・・
   Dim iByte As Integer
   Dim iIdxMax As Integer


   On Error GoTo ErrProc
      ・・・

   'ファイルのダウンロード
   iByte = 2
   iIdxMax = (WI_READ_BUFFSIZE / iByte) - 1

   Do
      'バッファエリア確保 & 初期化
      ReDim aiRecvData(iIdxMax)

      iReturn = InternetReadFile(lUrlHandle, aiRecvData(0), WI_READ_BUFFSIZE, lReadSize)
      lReadSum = lReadSum + lReadSize

      '終了判断
      If (lReadSize = 0) Or (iReturn = 0) Then Exit Do

      'サイズ調整
      If lReadSize < WI_READ_BUFFSIZE Then
         iIdxMax = Int(lReadSize / iByte) - ((lReadSize Mod iByte) > 0) - 1
         ReDim Preserve aiRecvData(iIdxMax)
      End If

      'ファイルに書き込み
      For i = LBound(aiRecvData) To UBound(aiRecvData)
         Put #iFP, ,aiRecvData(i)
      Next
   Loop
   Close #iFP
       ・・・
End Function

ポイントは、バッファ配列のサイズです。

そもそもバッファ配列は、API の処理でファイルの中身(バイナリデータ)を取得するメモリ領域確保が目的です。定数 WI_READ_BUFFSIZE に定義されている 1024 バイトのエリアがあればいいわけです。

今回、配列の型が Integer となり、2 バイトとなりますので、要素数は半分でいいことになります。

この必要な要素数の計算を行って保持している変数が iIdxMax となります。

ダウンロードファイルの最後の処理、取得したファイルサイズ lReadSize がバッファサイズに満たない場合、バッファサイズを次の通り算出しています。

   iIdxMax = Int(lReadSize / iByte) - ((lReadSize Mod iByte) > 0) - 1

Int 関数は切り捨てとなり、端数があると欠けてしまいます。そこで、割り切れない場合は、要素数を 1 加算させています(赤字部分)。

mod は剰余を求める演算子です。比較演算子は条件が成立すると True = -1 を返します。よって、あまりが発生した場合は -1、発生しない場合は 0 となります。それを減算(-)しているので、あまりが発生した場合だけ 1 加算するというわけですね。


このコードでは、ダウンロードサイズ lReadSize が奇数の場合、1 バイト多く処理されます。よって、完全な処理とは言えないと思います。ただ、これまでこのプログラムを運用していて、ファイルが壊れるなど問題は発生したことはありません。


まとめ

今回のような Windows API 連係の場合、必要なサイズの連続したメモリ領域を渡すことが重要となります。その手段として配列を使用しているということになります。

API コール時に引数で aiRecvData(0) を指定しているのは、そのメモリ領域の先頭アドレス(ポインタ)を渡しているということですね。

このあたりの挙動や仕組みが見えていると、API 利用時の応用が利くかもしれませんね...

0 件のコメント:

コメントを投稿