InternetReadFileで大きいファイルが読み取れない

このQ&Aのポイント
  • InternetReadFileでウェブ上の大きいファイルをローカルPCにコピーするプログラムが、ファイルサイズが大きくなると最後までコピーできません。
  • 読み取られたバイト数が0になり、それ以降のデータを読み取れません。
  • InternetReadFileで1Mバイト以上のファイルを読み取る方法をご教示ください。他の方法でも1Mバイト以上のファイルをhttpから取り込むことができる方法で結構です。
回答を見る
  • ベストアンサー

InternetReadFileで大きいファイルが読み取れない

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 ); } ...

質問者が選んだベストアンサー

  • ベストアンサー
  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.4

★今回は簡単にアドバイス ・『InternetReadFile』関数の戻り値を調べてみましょう。 ・『FALSE』が返ったらエラーです。 ・『GetLastError』関数で調べてみて下さい。 ・また、『Sleep』関数でウェイトを入れたらどうなる? ・あと、前回のサンプルで大きいサイトは正常にすべてを読み込めなかったかな? 最後に: ・InternetQueryDataAvailable( hUrl, &dwSize, 0, 0 ); で HTML ソースの  ファイルサイズから全体のどの部分で『例外発生』や読み込み停止になるのかを  解析してみましょう。→『Sleep』関数も使って。 ・以上。おわり。

jklm324
質問者

お礼

いろいろとご指導いただき、有難うございました。ループに最後に5msのSleepを入れることで解決しました。(もっと短くてもよさそうなので、最終値は調整中) >『InternetReadFile』関数の戻り値を調べてみましょう。 ファイルを読みきらずにdwByteがゼロになるときはFalseになっていました。 >『GetLastError』関数で調べてみて下さい。 12031 (ERROR_INTERNET_CONNECTION_RESET)でした。

その他の回答 (3)

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答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 ); ←読み込んだバイト数 }

jklm324
質問者

補足

結果は「デバッガでステップ実行すれば最後まで読み取れるが、実際に走らせると駄目」でした。実際に走らせた場合は、数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)
回答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 ); }

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.1

★助言 ・『fopen』の次の行の『fwrite』関数にある引数、『lpBuffer』、『dwBytesRead』の2つは  どこかで、宣言されていますか?また、『dwBytesRead』の値はセット済みですか? ・少し省略し過ぎです。→省略する場合は、『while』文の中だけか、または『fopen』文の中  だけを記述しましょう。 ・また、『while』文の中で、『lpBuffer』、『dwBytesRead』の2つを宣言していますが、  同じ名前の変数はブロック・スコープを用いて宣言しないようにしましょう。→混乱するため。 ・最後に一つ、無限ループの場合は『while(true)』よりも『for(;;)』で記述するとコンパイル時  ワーニング(警告)が出ません。→デバッグ時に助かります。『true』だと警告メッセージが出て  うっとうしいですよ。 お試し: ・サンプル・ソースを紹介します。 ・このサンプルは、『InternetQueryDataAvailable』関数でサイズを取得してから、メモリを  『GlobalAlloc』関数で確保して、そこのバッファに『InternetReadFile』関数でHTMLソース  を読み込みます。→その後、ファイルへ出力します。 ・まずは、このサンプルでウェブ上のHTMLソースをファイルにダウンロードできるか試して下さい。 ・また、結果報告の後に1Mバイト以上でも繰り返しでダウンロードできるサンプルも紹介します。 ・以上。おわり。結果報告をお願いします。→動作確認ですよ。

jklm324
質問者

お礼

早速の回答ありがとうございます。 文字数の制限内に収めようとして、切り詰めた結果判り難くなし申し訳ありませんでした。 いただいたサンプルで小さいファイルがダウンロードできることは確認できました。 ご指摘いただいた通りで、同じ変数名がブロック内で再宣言しているの確かにまずいですね。 省略されていますが、whileの前にInternetReadFileが追加されたのですが、そのときに削除すべきだった宣言が残されたままになっていたようです(汗)

関連するQ&A

  • InternetReadFileを使ったファイルダウンロード

    下のようなプログラムは、httpサーバから特定のファイルを ダウンロードすることが目的です。 (※referer, file名は仮のものです。) 試しに動かしてみて、転送速度が比較的速い場合には、一応問題なく ダウンロードすることができたのですが、転送速度が遅い場合には、 ファイルサイズがどんどん膨れ上がってしまいます。 問題は、InternetReadFileでブロックされることがないからだと 推測しましたが、情報が少なく困っています。 転送速度が遅い場合にも正常にダウンロードするにはどうしたら よいでしょうか? #include <windows.h> #include <wininet.h> #include <stdio.h> #include <stdlib.h> bool GetHttpFile(){   HINTERNET hInternet;   HINTERNET hFile;   char Buf[1000]; /* バッファ */   DWORD ReadSize;   BOOL bResult;   wchar_t szHead[] = TEXT("Referer:http://aaa.com/\r\n\r\n"); // ヘッダにRefererを追加する   FILE *fp;   bool ret;   /* 保存先ファイル作成/open */     fp = fopen("sample.zip", "wb");   if (fp == NULL){  /* ファイル作成/openに失敗 */     ret = false;        }else{ /* ファイル作成/openに成功 */     /* WININET初期化 */     hInternet = InternetOpen(       TEXT("Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)"),       INTERNET_OPEN_TYPE_PRECONFIG,       NULL,       NULL,       0);        /* URLのオープン */     hFile = InternetOpenUrl(       hInternet,       TEXT("http://aaa.com/bbb.zip"),       szHead,       0,       INTERNET_FLAG_RELOAD,       0);        /* オープンしたURLからデータを(1000バイトずつ)読み込む */     for(;;){       ReadSize = 1000;          bResult = InternetReadFile(         hFile,         Buf,         1000,         &ReadSize);          /* 全て読み込んだらループを抜ける */       if(bResult && (ReadSize == 0)) break;              /* ファイルに書き込み */       fwrite(Buf, sizeof(char), 1000, fp);     }        /* 後処理 */     InternetCloseHandle(hFile);     InternetCloseHandle(hInternet);     fclose(fp);     ret = true;   }      return ret; } int main(){   if(GetHttpFile() == true){     printf("成功\n");   }else{     printf("失敗\n");   }      return 0; }

  • ファイルサイズを変更したい

    visualstudio2010を使用しています。 c言語で書いているのですが、 test.txt ←abcdefg ファイルサイズ1KB Wtest.txt←書き込み用ファイル 上記のようなファイルがあり、バイナリでtest.txtを読み込みWtest.txtに書き込みたいと思っています。 その際に、1KBのtest.txtを5KBになるまでバイナリで「abcdefg」の後に0を代入したいのですがどう書けば良いのかわかりません。 FILE *fp, *fpw; char *fname = "test.txt"; char *fname_w = "Wtest.txt"; unsigned char buf[10000]; int size; fp = fopen( fname, "rb" ); if( fp == NULL ){ printf( "%sファイルが開けません\n", fname ); return -1; } fpw = fopen(fname_w, "wb"); if(fpw == NULL){ printf( "%sファイルが開けません\n", fname_w ); return -1; } size = fread( buf, sizeof( unsigned char ), sizeof (fp), fp ); /*ここに処理を追加したい*/ fwrite( buf, sizeof( unsigned char ), size, fpw);

  • ファイル

    ファイルを読み込み単語ごとに表示するプログラムです。 例 ファイル データ 形式 歴史・・ のように単語の後には空白がありますファイルです FILE *fp; char buf[1000]; char buf_word[1000]; char *str; char *bufstr; if((fp = fopen("test.txt","r")) == NULL){ printf("error!"); return 0; } while(fgets(buf,1000,fp) !=NULL){ str = buf; while(*str !='\0'){ strbuf = buf_word; if(*str ==' '){ printf("%s",buf_word); } else{ *strbuf++ = *str++; } } } とプログラムしてみましたが*strの値がどうもおかしく 最初が "フ" じゃなく"・"になってます。 最初の単語がG11とかなら"G"になっていますが・・ 教えて下さい。

  • ファイルコピープログラムについて

    まだC言語とか始めたばかりであまりよく分からないのですが、 今、ドラッグしたファイルをデスクトップにコピーするプログラムを作成してるのですが、少し困ったことになりました・・・・ 一応ファイルのコピーをデスクトップに作成はできるのですが、 何故か、作成されたファイルのサイズが2バイト程大きくなります・・・。 ファイルサイズを変えずにコピーする方法が分かりません・・・ ソースを張っておきますので、誰か解決策を教えていただけないでしょうか? #include <stdio.h> #include <windows.h> int main(int argc,char *argv[]) { FILE *fp,*copy; int buf; if(argc>1){ rename(argv[1],"DATA.bin"); fp=fopen("DATA.bin","rb"); copy=fopen("c:/xxx/yyy/Desktop/COPY.bin","wb"); while(1){ fread(&buf,sizeof(buf),1,fp); fwrite(&buf,sizeof(buf),1,copy); if(feof(fp)){ puts("OK"); fclose(fp); fclose(copy); rename("DATA.bin",argv[1]); rename("COPY.bin",argv[1]); exit(1); } } } return 0; }

  • ファイルを読み込むプログラムについて

    下のプログラムで分からない所がありますので、教えて頂ければと思います。宜しくお願い致します。 text = fgets(buf,256,fp);はfpのファイルから一行を読み込んでbufに格納するという処理ということは分かります。でも、while文内なので次にこの処理をする時に今度は、2行目(下の段)を読み込むはずですが、プログラム中のどこに2行目に移動させる処理があるのか分かりません。 予想ですが、text = fgets(buf,256,fp);の中にそのような意味の処理が含まれているのでしょうか? どなたかご教授お願い致します。 #include <stdio.h> int main(void) { FILE *fp; char buf[256]; char *text; char flname[256]; printf("ファイル名:"); gets(flname); fp = fopen(flname,"r"); do{ text = fgets(buf,256,fp); if(text != NULL){ printf("%s",text); } }while(text != NULL); fclose(fp); return(0); }

  • ファイルを読み込んで条件式を満たさない

    ファイルを読み込んで一部の文字列が来たら別のファイルの文字列を書き込んでもらうプログラムを作ろうとしたのですが、何故かifを使って条件分岐を試みたところ分岐してくれません。 どのようにしたら分岐しますか? 出来ればソースもお願いします。 ---ソースの内容--- #include <stdio.h> #include <string.h> void main(void){ FILE *fp,*fp2; char buf[100],buf2[100]; fp=fopen("yasa.txt","r+"); while( fgets( buf, 100, fp ) != NULL ){ if(strcmp(buf,"じゃがいも")==0){ fp2=fopen("kuda.txt","r+"); while( fgets( buf2, 100, fp2 ) != NULL ){ printf("%s",buf2); } fclose(fp2); } else{ printf("%s",buf); } } fclose(fp); } ---ソースここまで--- ---yasa.txtの内容--- きゃべつ にんじん じゃがいも だいこん セロリ ---yasa.txtここまで--- ---kuda.txtの内容--- もも オレンジ みかん ぶどう ---kuda.txtここまで---

  • Winsockを利用した単純なファイル送信プログラムについて

    こんばんは。 何度もこの掲示板を利用させていただいている者です。 WinsockのUDPを用いて簡単なファイル送信プログラムを作っています。UDPを使わずに、TCPを使用したほうが良いのでは?とのご指摘をいただきましたが、まずは、UDPを利用した単純なファイル送信プログラムを作ってみたいと思っています。 しかし、送信側から受信側へファイルがうまく受信できていません。もしかしたら、送信側自体がきちんと送信できていないのかもしれません。 以下にそのプログラムの概要と内容を示します。 [概要] 送信側→受信側にUDPを用いて、送信側にあるjpegまたはmpegファイルを送信し、受信側でファイルを開く。 [プログラム概要] ・送信側 ファイルポインタを用いてファイルオープン fread関数とsendto関数を用いて1024バイトずつ送信 ・受信側 ファイルポインタを用いてファイルオープン whileの無限ループ内に、recvfrom関数とfwrite関数を用いて送信側からのデータを受信 [プログラムの内容] ・送信側 printf("読み込み用ファイルを入力して下さい:"); scanf("%s",fname); if((fp = fopen(fname,"rb")) == NULL){ printf("入力ファイルをオープンできない。\n"); exit(1); } char send_buf[1025]; int n; while(n = fread(send_buf,1,1024,fp) != -1){ sendto(theSocket,send_buf,n,0,(LPSOCKADDR)&saServer,sizeof(struct sockaddr)); } ・受信側 char Recv_buf[1025]; char size; SOCKADDR_IN saClient; while(1){ size = recvfrom(theSocket,Recv_buf,1024,0,(LPSOCKADDR)&saClient,&nLen); fwrite(Recv_buf,size,1,fp); } ご指摘またはご教授をいただけたらと思います。 よろしくお願いします。

  • ファイルの受信

    c言語で、クライアント側のファイルを開き、内容をそのまま送信しているはずなのですが上手くいきません。 テキストファイルは正しく送れるようですが、他の実行ファイルなどはダメみたいです。 送信側は、"rb"でオープンし,whileで fread(send_buf,1024,1,fp); send(soc,send_buf,strlen(send_buf),0); を繰り返しています。send_buf[1025]です。 送信側は Recv_buf[1025];で size = recv(soc,Recv_buf,1024,0); fwrite(Recv_buf,size,1,fp); whileで繰り返し受信がなくなったらselectでタイムアウトしています。 いろいろ調べたのですがSleepが必要らしいそうですが・・・どうなんでしょう?

  • VisualC++ ファイル読み込み

    前回の質問が曖昧との意見もありもう1度きちんとした内容で質問したいと思います。現在VisualC++6.0でファイルの入出力について学んでおります。 void(){ FILE *p; CString a; cha buf[1001]; CFileDialog fdlg(TRUE,"","*.*"); if(fdlg.DoModal()!=IDOK) return; a=fdlg.GetPathName(); CEdit *p; p=(CEdit *)GetDlgItem(IDC_EDIT1); if((fp=fopen(a,"r"))==NULL) return while(fgets(buf,1000,fp)!=NULL){ buf[strlen(buf)-1]='\0'; strcat(buf,"\r\n"); int n=p->GetWindowTextLength(); p->SendMessage(EM_SETSEL,n,n); p->SendMessage(EM_REPLACESEL,0,(LPARAM)buf); } fclose(fp); } というプログラムでダイアログのEditボックスにファイルを読み込み 一行ずつ貼り付けていくという作業ができました。 このプログラムにさらに文字と数字を切り分けて表示する作業を組み込みたいのです。 例えば1行目に M02M61X0.F2.5B1FG1231などが書かれているテキストファイルを [M][02][M][61][x][0.][F][2.5][B][1][FG][1231] などのように分けて表示するプログラムです。 断片的なアドバイスで構いません。 教えてください

  • freadでファイルを読み込んだ際の処理時間について

    フラッシュメモリやハードディスクへファイルの書き出しをfwriteで行い、 作成したファイルをfreadを使って読み込み、その前後でclock関数を呼ぶことで 読み書きの処理時間を算出するプログラムを作成しています。 具体的には以下のような処理を行わせています。 //書き込み用のバッファを作成 buf_w = (unsigned char*)malloc(BUFSIZE); memset(buf,0,BUFSIZE); //ファイル書き込み処理 fp = fopen("ファイル名","wb"); start = clock(); fwrite(buf_w,BUFSIZE,1,fp); end = clock(); fclose(fp); //読み出し用のバッファを作成 buf_r = (unsigned char*)malloc(BUFSIZE); //ファイル読み出し処理 fp = fopen("ファイル名","rb"); start = clock(); fread(buf,BUFSIZE,1,fp); end = clock(); fclose(fp); 以上の処理にて、BUFSIZEが1GB程度の場合は問題ないのですが、 BUFSIZEを200MB程度に減らした場合に読み込み速度が ありえないほど速くなってしまうという問題が発生してしまいます。 (HDDやフラッシュメモリからの読み込みなのに転送速度が1000MB/sオーバーとか) RAMDISKとして設置したXドライブで試すと読み書き共に1200MB/sくらいになるので、 もしかしたらメモリにキャッシュされたデータを直接読み込んでしまっているのでは…… などと考えているのですが、試行錯誤すれども回避策が思い浮かびません。 どなたか、この現象について心当たりのある方がいらっしゃいましたら何かアドバイスいただけると幸いです。 宜しくお願いいたします。