- ベストアンサー
InternetReadFileで大きいファイルが読み取れない
- InternetReadFileでウェブ上の大きいファイルをローカルPCにコピーするプログラムが、ファイルサイズが大きくなると最後までコピーできません。
- 読み取られたバイト数が0になり、それ以降のデータを読み取れません。
- InternetReadFileで1Mバイト以上のファイルを読み取る方法をご教示ください。他の方法でも1Mバイト以上のファイルをhttpから取り込むことができる方法で結構です。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
★今回は簡単にアドバイス ・『InternetReadFile』関数の戻り値を調べてみましょう。 ・『FALSE』が返ったらエラーです。 ・『GetLastError』関数で調べてみて下さい。 ・また、『Sleep』関数でウェイトを入れたらどうなる? ・あと、前回のサンプルで大きいサイトは正常にすべてを読み込めなかったかな? 最後に: ・InternetQueryDataAvailable( hUrl, &dwSize, 0, 0 ); で HTML ソースの ファイルサイズから全体のどの部分で『例外発生』や読み込み停止になるのかを 解析してみましょう。→『Sleep』関数も使って。 ・以上。おわり。
その他の回答 (3)
- Oh-Orange
- ベストアンサー率63% (854/1345)
★新しいサンプルをご紹介します。 ・大きいファイルもダウンロードできるタイプです。 ・今回は、引数に『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 ); } まずは、ご報告まで。
- Oh-Orange
- ベストアンサー率63% (854/1345)
★サンプル・ソース /* 一度に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 ); }
- Oh-Orange
- ベストアンサー率63% (854/1345)
★助言 ・『fopen』の次の行の『fwrite』関数にある引数、『lpBuffer』、『dwBytesRead』の2つは どこかで、宣言されていますか?また、『dwBytesRead』の値はセット済みですか? ・少し省略し過ぎです。→省略する場合は、『while』文の中だけか、または『fopen』文の中 だけを記述しましょう。 ・また、『while』文の中で、『lpBuffer』、『dwBytesRead』の2つを宣言していますが、 同じ名前の変数はブロック・スコープを用いて宣言しないようにしましょう。→混乱するため。 ・最後に一つ、無限ループの場合は『while(true)』よりも『for(;;)』で記述するとコンパイル時 ワーニング(警告)が出ません。→デバッグ時に助かります。『true』だと警告メッセージが出て うっとうしいですよ。 お試し: ・サンプル・ソースを紹介します。 ・このサンプルは、『InternetQueryDataAvailable』関数でサイズを取得してから、メモリを 『GlobalAlloc』関数で確保して、そこのバッファに『InternetReadFile』関数でHTMLソース を読み込みます。→その後、ファイルへ出力します。 ・まずは、このサンプルでウェブ上のHTMLソースをファイルにダウンロードできるか試して下さい。 ・また、結果報告の後に1Mバイト以上でも繰り返しでダウンロードできるサンプルも紹介します。 ・以上。おわり。結果報告をお願いします。→動作確認ですよ。
お礼
早速の回答ありがとうございます。 文字数の制限内に収めようとして、切り詰めた結果判り難くなし申し訳ありませんでした。 いただいたサンプルで小さいファイルがダウンロードできることは確認できました。 ご指摘いただいた通りで、同じ変数名がブロック内で再宣言しているの確かにまずいですね。 省略されていますが、whileの前にInternetReadFileが追加されたのですが、そのときに削除すべきだった宣言が残されたままになっていたようです(汗)
お礼
いろいろとご指導いただき、有難うございました。ループに最後に5msのSleepを入れることで解決しました。(もっと短くてもよさそうなので、最終値は調整中) >『InternetReadFile』関数の戻り値を調べてみましょう。 ファイルを読みきらずにdwByteがゼロになるときはFalseになっていました。 >『GetLastError』関数で調べてみて下さい。 12031 (ERROR_INTERNET_CONNECTION_RESET)でした。