- PR -

ASPでのファイルダウンロード方法について

1
投稿者投稿内容
kaz
会議室デビュー日: 2008/09/11
投稿数: 7
投稿日時: 2008-09-12 19:13
ASPにてファイルのダウンロードサイトを構築したのですが、容量の大きいファイルのダウンロードが行えないという現象が発生しています。
(Response.BinaryWrite関数でエラーが発生。)
200MB以上のファイルは必ずダウンロードできず、100MB〜150MBのファイルはダウンロードできる時とできない時があります。

原因がわからないため、プログラムの変更を考えています。
以下のプログラム以外に、ダウンロードを行える方法はありますか?


■環境■
Windows2003 SP1
IIS 6.0
ASP(.netではないです。)


C:\WINDOWS\system32\inetsrv\MetaBase.xml
以下の設定値を変更
AspBufferingLimit="419430400"

■プログラムソース
Sub writeBinary(strFileName)

Dim Stream

'open file
Set Stream = Server.CreateObject("ADODB.Stream")
Stream.Open
Stream.Type = 1 ' StreamTypeEnum の adTypeBinary
Stream.LoadFromFile strFileName

'write header
strDownloadFileName = Mid(strFileName, InStrRev(strFileName, "\")+ 1)
Response.ContentType = "application/octet-stream" & vbCrLf & "Content-Disposition: attachment; filename=" & strDownloadFileName

' バイナリ型の Stream オブジェクトからを読み取って出力
Response.BinaryWrite Stream.Read
'★イベントビューアではこの行でエラーがでている。
'エラー内容「メモリ不足. 必要なメモリを割り当てられません。。」


' Stream を閉じる
Stream.Close
Set Stream = Nothing

Response.Flush

End Sub


サーバのイベントビューアには、以下のメッセージが出ます。
(※ダウンロードできない場合に必ずエラーがでている訳でなはないのですが。。)

------------------------------------------------------------------
イベントの種類: エラー
イベント ソース: Active Server Pages
イベント カテゴリ: なし
イベント ID: 5
日付: 2008/09/12
時刻: 17:54:10
ユーザー: N/A
コンピュータ: XXXXX
説明:
エラー : ファイル /xxx/xxx.inc 行 42 メモリ不足. 必要なメモリを割り当てられません。。

------------------------------------------------------------------
イベントの種類: 警告
イベント ソース: W3SVC-WP
イベント カテゴリ: なし
イベント ID: 2262
日付: 2008/09/12
時刻: 17:54:15
ユーザー: N/A
コンピュータ: XXXXX
説明:
次の理由のため、ISAPI 'C:\WINDOWS\system32\inetsrv\asp.dll' は自身が危険な状況であると報告しました : 'メモリ不足のため ASP に問題があります。'。

------------------------------------------------------------------

ブラウザには以下のメッセージが表示されます。

「ページを表示できません
 検索中のページには問題があるため表示できません。 」



[ メッセージ編集済み 編集者: kaz 編集日時 2008-09-12 19:14 ]
kiyokura
ベテラン
会議室デビュー日: 2007/08/08
投稿数: 69
お住まい・勤務地: 岡山
投稿日時: 2008-09-12 20:21
そのイベントログの状況だと、単純にメモリ不足に陥ってるように思います。
#そのあたりはパフォーマンスカウンタ等で調査してみていますでしょうか?


提示されているコードだと、レスポンスバッファがTrueであることが前提と思います。レスポンスバッファが有効な場合、Response.BinaryWriteの際にStreamからすべてのバイト配列をレスポンスバッファに読み、読み込み完了後にFlushする動きになると思います。


ですので、読み込むファイルサイズ分のメモリが最低限必要になる為、サイズが大きいとメモリの確保の失敗が発生するのではないかと推測します。


対処ですが……。
私自身そこまで気を遣わないといけない程のサイズのファイルをダウンロードさせようとしたことが無いので、幾分に推測含みになりますが、レスポンスバッファを使わないようにする(Response.Buffer=Falseにする)ことで、改善できるかもしれません。
kaz
会議室デビュー日: 2008/09/11
投稿数: 7
投稿日時: 2008-09-16 23:40
kiyokuraさん、回答ありがとうございます。

Response.Buffer=Falseに設定する方法を、以下のサイトを参考にテストしてみたところ、「Response.BinaryWrite」ではなく、「Stream.Read」でエラーが発生していることが判明しました。

Asp.netでは「Response.TransmitFile」関数でメモリに展開せずにできるとの記述があったので、調べているところです。

■参照サイト
http://cervi.jp/contents/2005/09/response_buffer.html

■プログラムソース
Sub writeBinary(strFileName)

Dim basp, data, bufferSize, offset, writeLen

'-- バッファリングをオフ
Response.Buffer = False

'-- Basp21 を作成
Set basp = Server.CreateObject("Basp21")

'-- データ読み込み
'open file
Set Stream = Server.CreateObject("ADODB.Stream")
Stream.Open
Stream.Type = 1 ' StreamTypeEnum の adTypeBinary
Stream.LoadFromFile strFileName

data = Stream.Read
' data = basp.BinaryRead(Server.MapPath(strFileName))

'-- ContentType, Content-Length を設定
strDownloadFileName = Mid(strFileName, InStrRev(strFileName, "\")+ 1)
Response.ContentType = "application/octet-stream" & vbCrLf & "Content-Disposition: attachment; filename=" & strDownloadFileName
Response.AddHeader "Content-Length", ubound(data) + 1

'-- 1000 バイトづつ出力
bufferSize = 500
For offset = 0 To ubound(data) Step bufferSize
writeLen = ubound(data) + 1 - offset
If writeLen > bufferSize Then
writeLen = bufferSize
End If
'-- バッファに出力
Response.BinaryWrite basp.MidB(data, offset, writeLen)
Next

'-- Basp21 を開放
Set basp = Nothing

' Stream を閉じる
Stream.Close
Set Stream = Nothing

End Sub

kiyokura
ベテラン
会議室デビュー日: 2007/08/08
投稿数: 69
お住まい・勤務地: 岡山
投稿日時: 2008-09-17 01:58
きよくらです。

引用:

Response.Buffer=Falseに設定する方法を、以下のサイトを参考にテストしてみたところ、「Response.BinaryWrite」ではなく、「Stream.Read」でエラーが発生していることが判明しました。



なるほど、これだと、ADODB.StreamのReadで結局変数に一気に読んでしまうので、ファイルサイズ分のメモリが必要になるのは変わらないですね……。

ということは、何らか読み込む時点でオフセット指定で読み込めれば解決しそうに思えます。

ADODB.Streamのリファレンスに上手くたどり着けないのでまた曖昧な記憶になるのですが、確かADODB.StreamだとReadメソッドに読み込みバイト数を指定できるオーバーロードがあった筈なので、それとオフセット位置を指定できるプロパティ(たぶんPositionだったと思います)を使えば、新しく提示されたコードの方法でいけるかもです。
#毎度、あやふやすみません。
kaz
会議室デビュー日: 2008/09/11
投稿数: 7
投稿日時: 2008-09-17 04:38
kiyokuraさん、回答ありがとうございます。

以下のプログラムで解決しました。
ありがとうございます。

■プログラムソース
Sub writeBinary(strFileName)

Dim data, bufferSize, offset, writeLen, fso, fileSize

'-- バッファリングをオフ
Response.Buffer = False

'-- データ読み込み
'open file
Set Stream = Server.CreateObject("ADODB.Stream")
Stream.Open
Stream.Type = 1 ' StreamTypeEnum の adTypeBinary
Stream.LoadFromFile strFileName

'-- ContentType, Content-Length を設定
strDownloadFileName = Mid(strFileName, InStrRev(strFileName, "\")+ 1)
Response.ContentType = "application/octet-stream" & vbCrLf & "Content-Disposition: attachment; filename=" & strDownloadFileName



'オブジェクト作成
Set objFSO = CreateObject("Scripting.FileSystemObject")

fileSize = objFSO.GetFile(strFileName).Size

offset = 0
bufferSize = 1000000
Do


If fileSize <= offset Then
Exit Do
End If

Stream.Position = offset

data = Stream.Read(bufferSize)

'-- バッファに出力
Response.BinaryWrite data


offset = offset + bufferSize
Loop

' Stream を閉じる
Stream.Close
Set Stream = Nothing

'Fileオブジェクトを開放
Set objFSO = Nothing

End Sub


[ メッセージ編集済み 編集者: kaz 編集日時 2008-09-17 09:37 ]
1

スキルアップ/キャリアアップ(JOB@IT)