InternetReadFileでウェブ上のファイルをローカルPCにコピーするプログラムが、ファイルサイズが大きくなると最後までコピーできません。具体的には1Mバイト以上のファイルの読み取りにおいて、150K~400Kバイトまでしか読み取ることができません。読み取られたバイト数(以下のプログラムでdwByteRead)が0になり、それ以降のデータを読み取れません。読み取られた総バイト数は読み取りバッファサイズ(以下のプログラムでREAD_BUF_SIZE)の整数倍ですが、その値は毎回異なります。(殆どの場合、150K~400Kバイト)dwByteReadが0なった後、10回再試行を行うルーチンを追加してみましたが、一旦0になるとそれ以降は復活することはなく効果無しでした。このプログラムは数10Kバイトまでの読み取りでは安定して動作しているので、プログラム自体の安定性は問題ないと思います。
InternetReadFileで1Mバイト以上のファイルを読み取ることに成功して方がいらっしゃいました、その方法をご教示ください。他の方法でも1Mバイト以上のファイルをhttpから取り込むことができる方法で結構です。
HINTERNET hSession = InternetOpen( "MyApp", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0 );
if( hSession ){
HINTERNET hService = InternetOpenUrl( hSession, url, NULL, 0, 0, 0 );
if( hService ){
...
if( (fp=fopen(fName.c_str(), "wb")) != NULL ){
fwrite( lpBuffer, dwBytesRead, 1, fp );
while(true){
Byte lpBuffer[READ_BUF_SIZE+1];
DWORD dwBytesRead = READ_BUF_SIZE;
InternetReadFile( hService, lpBuffer, READ_BUF_SIZE, &dwBytesRead );
if( dwBytesRead == 0 ) break;
fwrite( lpBuffer, dwBytesRead, 1, fp );
}
...
No.4ベストアンサー
- 回答日時:
★今回は簡単にアドバイス
・『InternetReadFile』関数の戻り値を調べてみましょう。
・『FALSE』が返ったらエラーです。
・『GetLastError』関数で調べてみて下さい。
・また、『Sleep』関数でウェイトを入れたらどうなる?
・あと、前回のサンプルで大きいサイトは正常にすべてを読み込めなかったかな?
最後に:
・InternetQueryDataAvailable( hUrl, &dwSize, 0, 0 ); で HTML ソースの
ファイルサイズから全体のどの部分で『例外発生』や読み込み停止になるのかを
解析してみましょう。→『Sleep』関数も使って。
・以上。おわり。
いろいろとご指導いただき、有難うございました。ループに最後に5msのSleepを入れることで解決しました。(もっと短くてもよさそうなので、最終値は調整中)
>『InternetReadFile』関数の戻り値を調べてみましょう。
ファイルを読みきらずにdwByteがゼロになるときはFalseになっていました。
>『GetLastError』関数で調べてみて下さい。
12031 (ERROR_INTERNET_CONNECTION_RESET)でした。
No.3
- 回答日時:
★新しいサンプルをご紹介します。
・大きいファイルもダウンロードできるタイプです。
・今回は、引数に『dwDebug』のデバッグ変数を付けてみました。
・通常は、『dwDebug=1』を指定します。『dwDebug=10』を指定すると
読み取りサイズが0バイトのとき、9回だけリトライさせることが出来ます。
・通信速度によって、途中で中断された場合も考慮してみましょう。
・また、サイトによっては上記のサンプルでは、正しく読み取れないこと
があります。この場合は、別の方法になりますが、あまりネットワークには
詳しくないため別の方法は今現在知りません。
・以上。おわり。まずは試してみて下さい。
★サンプル・ソース
/* 大きいHTMLソースをダウンロード */
extern DWORD MyDownloadFile( FILE fp, LPCTSTR lpURLName, DWORD dwDebug )
{
HINTERNET hInet, hUrl;
DWORD dwReadSize = 0; ←読み込んだバイト数⇒戻り値用
if ( (hInet = InternetOpen(TEXT("MyApp"),INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0)) != NULL ){
if ( (hUrl = InternetOpenUrl(hInet,lpURLName,NULL,0,0,0)) != NULL ){
TCHAR szBuff[ READ_BUF_SIZE ]; ←ここで宣言した方が良い。while文中で確保・解放が繰り返されるため。遅くなるよ。
DWORD dwSize;
do {
InternetReadFile( hUrl, szBuff, READ_BUF_SIZE, &dwSize );
fwrite( szBuff, dwSize, 1, fp );
dwReadSize += dwSize;
} while ( (dwSize != 0) || (--dwDebug != 0) );
InternetCloseHandle( hUrl );
}
InternetCloseHandle( hInet );
}
return( dwReadSize ); ←読み込んだバイト数
}
この回答への補足
結果は「デバッガでステップ実行すれば最後まで読み取れるが、実際に走らせると駄目」でした。実際に走らせた場合は、数100Kで停止するか(以前とほぼ同じ感じ)「モジュール'WININET.DLL'のアドレス 761A6531 でアドレス 00000000 に対する読み込み違反がおきました。」という例外発生で停止するかのいずれかです。ステップ実行とは具体的には、doループ内をすべてステップ実行するか、dwReadSize += dwSize;とfclose( fp );にブレークポイントを置いてfclose( fp );に至るまでGOをクリックし続けるかです。いずれでも最後まで行きました。
ということで、サンプルプログラムは問題なく動作するが、何かしら環境に問題あるようです。Borland C++Builder 6.0を使っているので、それとWININET.DLLの相性に問題があるのかもしれません。デバッガモードをオフにして作成した実行型を単独で走らせても同じ問題が発生するので、Borlandのデバッガ自体の問題ではないと思います。
以下が実際にテストに使われたプログラムです。(ファイルのOpen/Closeを追加し、Borland用に若干改造しました)READ_BUF_SIZEは1024/2048/4096/5000を試してみましたが、結果に違いは見られないようです。
int __fastcall TForm1::getFileFromWeb(AnsiString fName, AnsiString ansiURL)
{
char lpURLName[256];
strcpy(lpURLName, ansiURL.c_str());
HINTERNET hInet, hUrl;
DWORD dwReadSize = 0;
if ( (hInet = InternetOpen(TEXT("MyApp"),INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0)) != NULL ){
if ( (hUrl = InternetOpenUrl(hInet,lpURLName,NULL,0,0,0)) != NULL ){
FILE *fp;
if ( (fp = fopen(fName.c_str(),TEXT("wb"))) != NULL ){
TCHAR szBuff[ READ_BUF_SIZE ];
DWORD dwSize;
int dwDebug = 10;
do {
InternetReadFile( hUrl, szBuff, READ_BUF_SIZE, &dwSize );
fwrite( szBuff, dwSize, 1, fp );
dwReadSize += dwSize;
} while ( (dwSize != 0) || (--dwDebug != 0) );
fclose( fp );
}
InternetCloseHandle( hUrl );
}
InternetCloseHandle( hInet );
}
return( dwReadSize );
}
まずは、ご報告まで。
No.2
- 回答日時:
★サンプル・ソース
/* 一度にHTMLソースをダウンロード */
extern BOOL MyDownloadFile( LPCTSTR lpURLName )
{
HINTERNET hInet, hUrl;
BOOL bSuccess = FALSE;
DWORD dwSize;
LPTSTR lpBuff;
FILE fp;
if ( (hInet = InternetOpen(TEXT("MyApp"),INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0)) != NULL ){
if ( (hUrl = InternetOpenUrl(hInet,lpURLName,NULL,0,0,0)) != NULL ){
InternetQueryDataAvailable( hUrl, &dwSize, 0, 0 );
if ( dwSize != 0 ){
if ( (lpBuff = (LPTSTR)GlobalAlloc(GMEM_FIXED,dwSize)) != NULL ){
InternetReadFile( hUrl, lpBuff, dwSize, &dwSize );
if ( (fp = fopen(lpURLName,TEXT("wb"))) != NULL ){
fwrite( lpBuff, dwSize, 1, fp );
bSuccess = TRUE;
fclose( fp );
}
GlobalFree( lpBuff );
}
}
InternetCloseHandle( hUrl );
}
InternetCloseHandle( hInet );
}
return( bSuccess );
}
No.1
- 回答日時:
★助言
・『fopen』の次の行の『fwrite』関数にある引数、『lpBuffer』、『dwBytesRead』の2つは
どこかで、宣言されていますか?また、『dwBytesRead』の値はセット済みですか?
・少し省略し過ぎです。→省略する場合は、『while』文の中だけか、または『fopen』文の中
だけを記述しましょう。
・また、『while』文の中で、『lpBuffer』、『dwBytesRead』の2つを宣言していますが、
同じ名前の変数はブロック・スコープを用いて宣言しないようにしましょう。→混乱するため。
・最後に一つ、無限ループの場合は『while(true)』よりも『for(;;)』で記述するとコンパイル時
ワーニング(警告)が出ません。→デバッグ時に助かります。『true』だと警告メッセージが出て
うっとうしいですよ。
お試し:
・サンプル・ソースを紹介します。
・このサンプルは、『InternetQueryDataAvailable』関数でサイズを取得してから、メモリを
『GlobalAlloc』関数で確保して、そこのバッファに『InternetReadFile』関数でHTMLソース
を読み込みます。→その後、ファイルへ出力します。
・まずは、このサンプルでウェブ上のHTMLソースをファイルにダウンロードできるか試して下さい。
・また、結果報告の後に1Mバイト以上でも繰り返しでダウンロードできるサンプルも紹介します。
・以上。おわり。結果報告をお願いします。→動作確認ですよ。
早速の回答ありがとうございます。
文字数の制限内に収めようとして、切り詰めた結果判り難くなし申し訳ありませんでした。
いただいたサンプルで小さいファイルがダウンロードできることは確認できました。
ご指摘いただいた通りで、同じ変数名がブロック内で再宣言しているの確かにまずいですね。 省略されていますが、whileの前にInternetReadFileが追加されたのですが、そのときに削除すべきだった宣言が残されたままになっていたようです(汗)
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- PHP ここでの ②if($su_d<>"")の比較演算子 を使う理由は 1 2022/03/26 02:33
- PHP htmlspecialcharsが機能していないです。 バグですか? 1 2022/04/05 01:22
- その他(プログラミング・Web制作) pythonでクラスで複数のメソッドを利用する方法 2 2022/04/15 04:17
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- Visual Basic(VBA) Excel-VBAでのファイルの開き方 4 2023/02/14 11:01
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
- PHP if(preg_match("/[^0-9]/",$gu_d)){意味を教えてください。 1 2022/05/06 05:37
- Excel(エクセル) 2つのVBAを一緒にしたら機能しなくなりました(エクセル) 7 2022/06/02 12:41
- C言語・C++・C# 至急お願いします。C言語で.imgのファイルを読み込んで1バイトづつ出力するプログラムを作りたいので 3 2023/01/16 22:49
- Excel(エクセル) PHPプログラムをエクセルに張り付けると検索ボックスがでてくる! 3 2022/05/08 07:10
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
バイナリファイルをテキストフ...
-
改行までの一文字ずつのファイ...
-
c言語 2つのファイルを行ご...
-
C言語初心者の質問失礼します。
-
MacからWinにファイルを添付す...
-
バッファとは何ですか
-
Microsoft VBAで2GBを超えるフ...
-
マイクラでPythonのプログラミ...
-
xismoについてです!開こうとす...
-
セルに入力されたパスでフォル...
-
どんなプログラムを書いても指...
-
エクセルの実行ファイルについ...
-
グローバル変数のよくない使い...
-
ファイルクローズとメモリ使用...
-
RS-232Cで接続したPC間のファイ...
-
ファイルが開かない
-
C言語のローカル変数初期化に...
-
VBフォームアプリケーションに...
-
MATLABのsaveでファイル名を試...
-
64bit環境で32bitのodbc参照
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
ファイル内のデータを1行削除...
-
バイナリファイルをテキストフ...
-
fgetsで2行目から文字化け
-
テキストファイルの行数を取得...
-
c言語 2つのファイルを行ご...
-
C言語での改行コードの扱いにつ...
-
改行までの一文字ずつのファイ...
-
VBSで指定行に挿入
-
【VB.Net】バイト型配列に読み...
-
freadとfwrite
-
巨大なテキストファイル(可変...
-
0バイトファイルの作成
-
fopen(書き込みモード)でファイ...
-
fopenで開いたファイルのサイズ...
-
winsock recvでの文字化け
-
ファイルサイズ指定し、ファイ...
-
fortranで文字列を読み込む際の...
-
【C言語】テキスト読み込みの行...
-
C言語での採番について
-
VS2010 MFC CStdioFileについて
おすすめ情報