←検索窓の楽しみ方
  ショッピングモール  掲示板ランキング


掲示板利用宣言

 次のフォームをすべてチェックしてからご利用ください。

 私は

 題名と投稿者名は具体的に書きます。
 課題の丸投げはしません。
 ソースの添付は「HTML変換ツール」で字下げします。
 返信の引用は最小限にします。
 環境(OSとコンパイラ)や症状は具体的に詳しく書きます。
 返信の付いた投稿は削除しません。
 マルチポスト(多重投稿)はしません。

掲示板1

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧

No.4688

ファイルアップロードについて
投稿者---かおり(2005/10/06 21:21:56)


ファイルアップロードするCGI をC 言語で作成しています。どうしても意図した動きをしてくれないので、質問させてください。

フォームを含むHTML ファイルから、画像などのファイルをアップロードし、C 言語のCGI で受け取った画像データをファイルにPUT するような処理を実現しようとしています。

しかし、実際POST で受け取ったデータ長(CONTENT_LENGTH) が5000 Byte ほどあるのに、fread でマルチパートヘッダを含むデータを取得しても、取得後のデータ長が100 程度にしかなりません。結果、CGI で画像データが取得できず困っています。

画像データをCGI で取得する問題の箇所は下記です。

// POST 文字長を取得する
if( (length=getenv("CONTENT_LENGTH")) == NULL ) return -1;
fprintf( stderr, "取得前文字長 = %d?n", atoi(length) );

// POST 文字列を格納する領域を作成する
if( (*query=(char *)malloc(sizeof(char)*(atoi(length)+2))) == NULL ) return -1;

fread( *query, sizeof(int), atoi(length), stdin );
*((*query)+atoi(length)) = '?0';

fprintf( stderr, "取得後文字長 = %d?n", strlen(*query) );

取得前文字長 != 取得後文字長 となり、結果画像データは正常に取得されません。お手数かけますが、原因がお分かりの方、教えてくれないでしょうか?


この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:ファイルアップロードについて 4689 si 2005/10/06 22:37:12
<子記事> Re:ファイルアップロードについて 4691 si 2005/10/06 23:18:05
<子記事> Re:ファイルアップロードについて 4692 nop 2005/10/07 00:22:22


No.4689

Re:ファイルアップロードについて
投稿者---si(2005/10/06 22:37:12)


>length=getenv("CONTENT_LENGTH")
データのバイト長
>if( (*query=(char *)malloc(sizeof(char)*(atoi(length)+2))) == NULL ) return -1;
*query ? query は char **query ?
何度も atoi(length) と同じことを繰り返すのはどんなものか?
>fread( *query, sizeof(int), atoi(length), stdin );
sizeof(int) ?


この投稿にコメントする

削除パスワード

No.4690

Re:ファイルアップロードについて
投稿者---si(2005/10/06 22:59:41)


参考:
u_long upsize = atol(getenv("CONTENT_LENGTH"));
char *readbuffer = malloc(upsize + 1);
u_long rdsize = fread( readbuffer, 1, upsize, stdin );
if ( rdsize == upsize){
   readbuffer[upsize] = '\0';
   return readbuffer;
   }
else{
   free(readbuffer);
   return NULL;
   }



この投稿にコメントする

削除パスワード

No.4700

Re:ファイルアップロードについて
投稿者---かおり(2005/10/07 09:59:22)


>参考:
>
u_long upsize = atol(getenv("CONTENT_LENGTH"));
char *readbuffer = malloc(upsize + 1);
u_long rdsize = fread( readbuffer, 1, upsize, stdin );
if ( rdsize == upsize){
   readbuffer[upsize] = '?0';
   return readbuffer;
   }
else{
   free(readbuffer);
   return NULL;
   }


私のコードに上記コードを組み込んでみました。下記のようになりました。

// POST 文字長を取得する
upsize = atol( getenv("CONTENT_LENGTH") );
fprintf( stderr, "取得前文字長 = %ld?n", upsize );

// POST 文字列を格納する領域を作成する
if( (*query=(char *)malloc(sizeof(char)*(upsize+1))) == NULL ) return -1;

rdsize = fread( *query, 1, upsize, stdin );
if( rdsize != upsize ) { free( *query ); return -1; }

*((*query)+upsize) = '?0';

fprintf( stderr, "取得後文字長 = %ld?n", rdsize );

確かに、上記ですと、upsize == rdsize となっています。ただ、下記のようにprintf でデータを見たときに、HTTP 形式の書式でデータが閲覧できないようですが、これは当然のことなのでしょうか?

コード:
fprintf( stderr, "デコード文字列 ?n %s?n", *query );

結果:
デコード文字列

Content-Disposition: form-data; name="text5"; filename="test.jpg"
Content-Type: image/jpeg

〓〓〓〓

上記のように、マルチパートのバウンダリが末尾に出力されていないのでCGI で画像データがとれず困っていました。そもそも、この出力が正常なのかですら今は判断できていません。

助言していただけるようでしたら、お願いします。


この投稿にコメントする

削除パスワード

No.4698

Re:ファイルアップロードについて
投稿者---かおり(2005/10/07 09:41:34)


お返事ありがとうございます。かおりです。

>>length=getenv("CONTENT_LENGTH")
>データのバイト長
>>if( (*query=(char *)malloc(sizeof(char)*(atoi(length)+2))) == NULL ) return -1;
>*query ? query は char **query ?

そうです。URL デコードする処理を関数として実装し、引数としてchar **query で受け取り、関数内部でメモリ確保を行いたかったので、*query という書き方をしています。誤解を招くような記述で申し訳ありません。

>何度も atoi(length) と同じことを繰り返すのはどんなものか?

確かに、これではコストがかかると思いました。

>>fread( *query, sizeof(int), atoi(length), stdin );
>sizeof(int) ?

そもそも、fread の第二引数には、stdin からのデータ読み込み単位を指定するとありました。バイナリデータを読み込むときの単位ってどう指定したらよいのでしょうか?読み込んだデータを記憶するfread の第一引数の単位(char) にあわせるのが良いのでしょうか?

勉強不足で申し訳ないですが、お願いします。


この投稿にコメントする

削除パスワード

No.4691

Re:ファイルアップロードについて
投稿者---si(2005/10/06 23:18:05)


肝心な部分を忘れてました
>fprintf( stderr, "取得後文字長 = %d?n", strlen(*query) );
strlen(*query) ?
*query はバッファへのポインタでは?
binary データを strlen?


この投稿にコメントする

削除パスワード

No.4692

Re:ファイルアップロードについて
投稿者---nop(2005/10/07 00:22:22)


まず、HTTPの規格は調査しましたか?
それから、フォームのエンコード指定はマルチパートになっていますか?
また、パケットモニタなどで実際のパケットデータは見てみましたか?


>fprintf( stderr, "取得後文字長 = %d?n", strlen(*query) );

このコードで表示されるのは、データ長ではなく、
文字列として見た場合の文字列長です。
画像データは、大抵バイナリデータとして送信されます。
さらに、フォームのエンコード指定によっては、
エンコードされている場合が考えられますが、
その辺は問題ないですか?

# ついでに、fread()で「CONTEXT_LENGTH」分のデータをリードし、
# そのデータをダンプ表示してみる事もお勧めします。


それから、CGI関連でしたらHTMLのソースも一緒に載せた方が、
より明確な回答が得られると思います。


この投稿にコメントする

削除パスワード

No.4693

Re:ファイルアップロードについて
投稿者---nop(2005/10/07 00:27:08)


参考までに...
HTTP日本語訳


この投稿にコメントする

削除パスワード

No.4699

Re:ファイルアップロードについて
投稿者---かおり(2005/10/07 09:52:05)


お返事ありがとうございます。かおりです。

>まず、HTTPの規格は調査しましたか?

大体、HTTP の規格は勉強したつもりですが、ファイルアップロードの処理が内部的にどのように行われているかはあまり理解していないかもしれません。

>それから、フォームのエンコード指定はマルチパートになっていますか?

なっているはずです。念のため、下記がファイルアップロードするHTML のソースです

<html>
<body>
<form action="./index.cgi" enctype="multipart/form-data" method="post">

<input type="file" name="text5">
<br><br>

<input type="submit" value="送信する">
</form>
</body>
</html>

>また、パケットモニタなどで実際のパケットデータは見てみましたか?

もうしわけありませんが、そこまでスキルがないもので。。
動作環境はLinux なのですが、どのようにパケットデータを見ることができるのでしょうか?
データは普通にprintf でプリントして見ることしか試したことがありません。

>>fprintf( stderr, "取得後文字長 = %d?n", strlen(*query) );
>
>このコードで表示されるのは、データ長ではなく、
>文字列として見た場合の文字列長です。

すいません。勉強不足で申し訳ないのですが、文字列長とデータ長の違いがいまいち理解できてないです。参考になるサイトとかありましたら教えていただけないでしょうか?

>画像データは、大抵バイナリデータとして送信されます。
>さらに、フォームのエンコード指定によっては、
>エンコードされている場合が考えられますが、
>その辺は問題ないですか?

フォームのエンコードとは、<form > タグで指定する enctype="multipart/form-data" のことでしょうか?

># ついでに、fread()で「CONTEXT_LENGTH」分のデータをリードし、
># そのデータをダンプ表示してみる事もお勧めします。

その方法がわかりません。よろしければ参考サイトなど教えていただけないでしょうか?

お願いします。


この投稿にコメントする

削除パスワード

No.4701

Re:ファイルアップロードについて
投稿者---si(2005/10/07 10:54:51)


multipart/form-data からのデータの構造は

-----------------------------108023735212354842201367324360^M^J
Content-Disposition: form-data; name="filename"; filename="upload.html"^M^J
Content-Type: text/html^M^J
^M^J
<?xml version="1.0" encoding="UTF-8"?>^M^J
.....中略....
</html>
^M^J
-----------------------------108023735212354842201367324360^M^J
Content-Disposition: form-data; name="overwrite"^M^J
^M^J
true
^M^J
-----------------------------108023735212354842201367324360^M^J
Content-Disposition: form-data; name="comment"^M^J
^M^J
アップロード HTML
^M^J
-----------------------------108023735212354842201367324360--^M^J
注:^M はキャリッジリターンコード(CR)0x0D、^J はラインフィードコード(LF)0x0A
のように、boundary文字列が区切りとなっている、ブロックの並びになっています。
各ブロックは、
boundary文字列^M^J
Content-Disposition: 行^M^J  <ー 変数名やファイル名が含まれる
Content-Type: 行^M^J      <- ファイルデータの場合 MINE型が入る
^M^J          <- データとの区切りの空行
データ         <- 変数データ
^M^J          <- データとの区切りの空行
boundary文字列--^M^J
と、CRLFが区切りとなっています。
データは、何の変更もされていないので、画像であれば printf はできません。


この投稿にコメントする

削除パスワード

No.4702

Re:ファイルアップロードについて
投稿者---nop(2005/10/07 11:28:47)


>動作環境はLinux なのですが、どのようにパケットデータを見ることができるのでしょうか?

パケットモニタやパケットキャプチャ等という分類のソフトをインストールしたり、
Linuxでしたらカーネル内のドライバのソースを修正し、
カーネルコンパイルして再起動するとモニタ出来るかもしれません。

# もっとも、パケットのキャプチャが判らない様だと、
# 敷居が高く無理難題に近いと思いますが...


>すいません。勉強不足で申し訳ないのですが、文字列長とデータ長の違いがいまいち理解できてないです。

まず、Cに於ける文字列の定義は理解していますか?
文字列長と言うと、'\0'までのデータ長で、
データ長と言うと、有効なデータの長さを言います。
この時、'\0'に相当する値も有効データと見なします。


>># そのデータをダンプ表示してみる事もお勧めします。
>
>その方法がわかりません。よろしければ参考サイトなど教えていただけないでしょうか?

単に読み込んだデータをprintf()等で16進数の羅列として表示すれば良いでしょう。
# 余力があれば ASCII 表示できれば、なおいっそう理解が深まるでしょう。



とりあえず、あなたの回答から察すると、
もっと基礎知識が必要かと思われます。
最低でも、バイナリとストリングの違いくらいは勉強するべきです。


この投稿にコメントする

削除パスワード

No.4703

Re:ファイルアップロードについて
投稿者---si(2005/10/07 13:02:28)


以前作った、C & CGI初心者向け FORM 生データ表示サンプルです。
ご参考までに
pr_form_data.c
/*
環境変数およびFORMによるアップロードデータを表示するCGIソース
マルチバイト文字非対応
*/
#include <stdio.h>
#include <stdlib.h>
#define FORM_MAX_SIZE (1024*8) // アップロードデータの制限

int main(int argc,char *argv[],char *envp[]){
    int i,len,n;
    char *method,*env,*buf;
    void dump_post_data(unsigned char *buf,int len);
    // 標準出力にお決まりの文章を書き出す 
    printf("content-type: text/html\n");
    printf("\n");   // cgiでは、ここに空白行を入れないと正しく表示されない
    printf("<html>\n" );
    printf("<head>\n" );
    printf("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
    printf("<title>print cgi env</title>\n" );
    printf("</head>\n" );
    printf("<body>\n" );
    printf("<h2>環境変数の表示</h2>");
    for ( i = 0; envp[i]; i++){
        printf("%d %s<br />\n", i+1, envp[i]);
        }
    method = getenv("REQUEST_METHOD");
    if (method){
        if (!strcmp(method,"GET")){
            if ((env = getenv("QUERY_STRING")) != NULL){
                printf("<h2>Method GET QUERY_STRING 表示</h2>");
                printf("%s<br />\n",env);
                }
            else{
                printf("<h2>QUERY_STRING取得失敗</h2>");
                }
            }
        else if (!strcmp(method,"POST")){
            env = getenv("CONTENT_LENGTH");
            len = atoi(env);
            if (env && len > 0){
                if (len < FORM_MAX_SIZE){
                    if ((buf = malloc( len + 1)) != NULL){
                        n = fread( buf, 1, len, stdin);
                        buf[n] = '\0';
                        if ((n == len) && ((env = getenv("CONTENT_TYPE")) != NULL)){
                            if (strcasestr(env,"multipart")){
                                dump_post_data((unsigned char *)buf,len);
                                }
                            else{
                                printf("<h2>Method POST URL ENCODE データ表示</h2>");
                                printf("%s<br />\n",buf);
                                }
                            free(buf);
                            }
                        else{
                            printf("<h2>Method POST read失敗</h2>");
                            free(buf);
                            }
                        }
                    else{
                        printf("<h2>Method POST malloc失敗</h2>");
                        }
                    }
                else{
                    printf("<h2>Method POST アップロードサイズが大きすぎる</h2>");
                    }
                }
            else{
                printf("<h2>Method POST CONTENT_LENGTH取得失敗</h2>");
                }
            }
        else{
            printf("<h2>不正なREQUEST_METHOD</h2>");
            }
        }
    else{
        printf("<h2>REQUEST_METHOD取得失敗</h2>");
        }
    printf("</body>\n" );
    printf("</html>\n" );    
    return 0;
    }
void dump_post_data(unsigned char *buf,int len){
    unsigned char *p,*q;
    unsigned char *end   = &buf[len-1];
    int i = 0;
    unsigned char str[33];
    int hex;
    printf("<h2>Method POST multipart/form-data DUMP</h2>\n");
    for (p = buf; p <= end; p++){
        if (i < 32){
            if ( *p < 0x10)
                printf("0%x ",*p);
            else
                printf("%x ",*p);
            if ( *p < ' ' || 0x7E < *p)
                str[i] = '.';
            else
                str[i] = *p;
            i++;
            }
        else{
            str[i] = '\0';
            printf("| ");
            for (q = str; *q; q++){
                if ( *q == '<')
                    printf("&lt;");
                else if ( *q == '>')
                    printf("&gt;");
                else if ( *q == '&')
                    printf("&amp;");
                else if ( *q == '\\')
                    printf("&quot;");
                else
                    putchar(*q);
                }
            printf("<br />\n",str);
            i = 0;
            p--;
            }
        }
    str[i] = '\0';
    for ( ; i < 32; i++){
        printf("...");
        }
    printf("| %s<br />\n",str);
    }



この投稿にコメントする

削除パスワード

No.4708

Re:ファイルアップロードについて
投稿者---かおり(2005/10/07 16:47:41)


お返事ありがとうございます。かおりです。

提示していただいたソースの意味を解読しながら、独自で勉強して、簡単なソースを作成してみました。

しかし、うまく動作してくれません。なぜでしょうか?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// ● 外部変数定義

char            errmsg[1024];                       // エラーメッセージ

int main( int argc, char **argv ) {
    int              status;                        // ステータス
    u_long           rdsize;                        // 読み込むべきサイズ
    u_long           wtsize;                        // 読み込んだサイズ
    unsigned char    *query;                        // 読み込んだデータ
    unsigned char    string[9999];                  // 一時作業配列 
    int              ii;                            // 一時作業変数
    int              jj;                            // 一時作業変数
    unsigned char    *end;                          // 一時作業変数
    unsigned char    *ptr;                          // 一時作業ポインタ

    // 変数を初期化する
    status        = 1;
    rdsize        = 0;
    wtsize        = 0;
    query         = NULL;
    string[0]     = '?0';
    ii            = 0;
    jj            = 0;
    end           = NULL;
    ptr           = NULL;
    errmsg[0]     = '?0';

    printf( "Content-Type: text/html; charset=EUC-JP?n?n" );

    // ● URL デコードを行う

    rdsize = atoi( getenv( "CONTENT_LENGTH" ) );
    printf( "読み込むべきデータ長 = %d<br>", rdsize );

    query = (char *)malloc( sizeof(char)*(rdsize+1) );
    if( query == NULL ) {
        sprintf( errmsg, "%d 行目:致命的エラーが発生しました。", __LINE__ );
        goto EXIT;
    }

    // ● データを読み込む

    wtsize = fread( query, sizeof(char), rdsize, stdin ); query[wtsize] = '?0';
    printf( "読み込んだデータ長 = %d<br>", wtsize );

    if( wtsize != rdsize ) {
        sprintf( errmsg, "%d 行目:致命的エラーが発生しました。", __LINE__ );
        goto EXIT;
    }

    // ● マルチパートデータを表示する

    end = &query[rdsize-1];    
    for( ptr=query; ptr<=end; ptr++ ) {
        if( *ptr < 0x10 ) printf( "0" );
        printf( "%x ", *ptr );

        if( *ptr < ' ' || 0x7E < *ptr ) {
            string[jj] = '.';
        } else {
            string[jj] = *ptr;
        }

        jj ++;
    }
    string[jj] = '?0';

    printf( "<br><br>" );

    for( ptr=string; *ptr; ptr++ ) {
        putchar( *ptr );
    }



    printf( "正常に終了しました<br>" );

EXIT:

    if( errmsg[0] != '?0' ) printf( "%s<br>", errmsg );

    exit( 0 );
}



この投稿にコメントする

削除パスワード

No.4710

Re:ファイルアップロードについて
投稿者---かおり(2005/10/07 17:00:12)


かおりです。

わかりました。すごく基本的なことでミスしていたようです。
16 進数でダンプしたデータの中に、00 があったので文字列として表示する際にはそこが文末として認識していただけでした。

今回はいろいろ勉強になりました。あとは、標準入力から得られたバイナリデータだけをソース中で読み込む方法を検討してみます。

ありがとうございました。


この投稿にコメントする

削除パスワード

No.4709

Re:ファイルアップロードについて
投稿者---かおり(2005/10/07 16:51:16)


お返事ありがとうございます。かおりです。

>パケットモニタやパケットキャプチャ等という分類のソフトをインストールしたり、
>Linuxでしたらカーネル内のドライバのソースを修正し、
>カーネルコンパイルして再起動するとモニタ出来るかもしれません。

私の知識では到底むりな話ですね。


>まず、Cに於ける文字列の定義は理解していますか?
>文字列長と言うと、'?0'までのデータ長で、
>データ長と言うと、有効なデータの長さを言います。
>この時、'?0'に相当する値も有効データと見なします。

データ長、文字長の意味はわかりました。そして、ご教授いただいてから、バイナリデータにおけるC 言語での取り扱いについて、勉強しました。まだまだ知らないことばかりで、勉強不足と痛感するばかりです。

>単に読み込んだデータをprintf()等で16進数の羅列として表示すれば良いでしょう。
># 余力があれば ASCII 表示できれば、なおいっそう理解が深まるでしょう。

文字単位で%x で出力するのですね。ASCII コードであれば、%d で表示すれば確認できることはわかりました。ありがとうございます。

>もっと基礎知識が必要かと思われます。
>最低でも、バイナリとストリングの違いくらいは勉強するべきです。

はい。勉強、がんばります。


この投稿にコメントする

削除パスワード

No.4723

Re:ファイルアップロードについて
投稿者---nop(2005/10/09 00:32:22)


>># 余力があれば ASCII 表示できれば、なおいっそう理解が深まるでしょう。
>
>ASCII コードであれば、%d で表示すれば確認できることはわかりました。

ここで言う ASCII 表示は、16進数の羅列の後に、
ASCII 文字表示もあればよいね、と言う(ry

# ちなみに、以下のようなイメージ
#
## xxxxxxxx : xx xx xx xx xx xx xx xx - xx xx xx xx xx xx xx xx : aaaaaaaaaaaaaaaa
## xxxxxxxx : xx xx xx xx xx xx xx xx - xx xx xx xx xx xx xx xx : aaaaaaaaaaaaaaaa
## xxxxxxxx : xx xx xx xx xx xx xx xx - xx xx xx xx         : aaaaaaaaaaaa



この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧




掲示板提供:Real Integrity