[PHP-users 2340] php3 で10人同時にファイルアップロードすると異常。

KIM php-users@php.gr.jp
Sat, 29 Sep 2001 14:08:18 +0900


木村@ISTと申します。

既知の問題でしたら、ご容赦ください。

現在、WinNTSv4.0 SP6a+IIS4+php3.0.18-i18n-jaで動作させていますが、
ファイルアップロードを10人くらい同時に実行させると、動きがおかしいです。

以下にソースを抜書きします。

送信処理部

    <FORM ID="FRM" METHOD=POST ACTION=edit_msg.php3 NAME=edit_msg ENCTYPE=multipart/form-data onSubmit="return CheckFields();">
    氏名<BR>
    <INPUT TYPE=TEXT NAME=frmName SIZE=30><BR><BR>
    題名<BR>
    <INPUT TYPE=TEXT NAME=frmSubject SIZE=40><BR><BR>
    画像<BR>
    <INPUT TYPE=FILE NAME=frmPict SIZE=50><BR><BR>

    <INPUT TYPE=SUBMIT VALUE=送信>
    <INPUT TYPE=RESET  VALUE=クリア>

    </FORM>

受け取り処理部

function funcGetNamePath($messageId, $localFullPath, &$filename, &$path, &$urlpath) {
  $filename = GetBaseName($localFullPath);
  $name = strval($messageId) . "_0." . GetExtention($localFullPath);
  $path = DIR_DATA . "/pict/$name";
  $urlpath = "/pict/$name";
}

function funcInsert() {
  global $cookUserId
  global $frmName, $frmSubject, $frmPict, $frmPict_name;
        
  // 接続
  $connection = pg_connect(DB_HOST,"","","",DB_NAME);

  // 一意のID生成
  $queryMessage  = "EXEC GETUNIQID";
  $resultMessage = pg_exec($connection, $queryMessage);
  $maxMessageId  = pg_result($resultMessage, 0 , 0);

  $queryMessage = "INSERT INTO msg (message_id,user_id,sender_name,subject) VALUES ";
  $queryMessage = "$queryMessage ($maxMessageId,$cookUserId,'$frmName','$frmSubject')";

  pg_exec($connection, $queryMessage);

  // 一意の格納場所生成
  funcGetNamePath($maxMessageId, $frmPict_name, $pictureName, $picturePath, $uriPath);

  // 格納
  if(copy($frmPict, $picturePath)) {
    $queryMessage = "INSERT INTO msg_picture VALUES " .
                    "($maxMessageId, '$pictureName',"'$uriPath')";
    pg_exec($connection, $queryMessage);
  }


フォームを使用して、ユーザ名とタイトル、画像ファイルを登録させる処理なのですが、
$frmPict_nameに指定したファイル名が返ってきていて、
実際のサーバ上のファイル名が$frmPictに返ってきます。
$frmPictをcopyするのですが、10人中5,6人がそのうちの1人が
指定したファイルをcopyしているようで、同じファイルを登録したことになってしまいます。

1人や2人でやっている場合には、問題がないので、スクリプトの問題では
無いような気がします。

念のため、php3.0.18のソースを見たのですが、post.cに以下の処理があります。

> [php-3.0.18-i18n-ja]
> --- function/post.c:53 -------
> static void php3_getput(void) {
> #if MODULE_MAGIC_NUMBER > 19961007
>     char upload_buffer[BUFSIZ];
> #endif
>     size_t bytes=0;
>     char *fn;
>     FILE *fp;
>     int length, cnt;
> 
>     length = GLOBAL(request_info).content_length;
>     if (length > php3_ini.upload_max_filesize) {
>         php3_error(E_WARNING, "Max file size of %ld bytes exceeded - temporary file not saved", php3_ini.upload_max_filesize);
>         SET_VAR_STRING("PHP_PUT_FILENAME", estrdup("none"));
>         return;
>     }
>     fn = tempnam(php3_ini.upload_tmp_dir, "php");
>     fp = fopen(fn, "w");
>     if (!fp) {
>         php3_error(E_WARNING, "File Upload Error - Unable to open temporary file [%s]", fn);
>         return;
>     }
> ..
> 

このtempnamは、テンポラリのファイル名を生成するだけですので、
fopenに時間がかかる場合、複数セッションに同じテンポラリファイル名を
返す、ってことはないのでしょうか?

ちなみに、php4では、実際に作ってます。
php_open_temporary_file内のphp_do_open_temporary_fileで、
Win32の場合、GetTempFileName(path,pfx,0,opend_path)を呼んでおり、
ファイルが開かれた状態で返ってくるようです。

> [php-4.0.6]
> ----- main\rfc1867.c -----
> 
> case 3:            /* Handle file */
>     loc = memchr(ptr, *boundary, rem);
>     u = ptr;
>     while (loc) {
>         if (!strncmp(loc, boundary, len)
> #if NEW_BOUNDARY_CHECK
>             && (loc-2>buf && *(loc-2)=='-' && *(loc-1)=='-') /* ensure boundary is prefixed with -- */
>             && (loc-2==buf || *(loc-3)=='\n') /* ensure beginning of line */
> #endif
>             ) {
>             break;
>         }
>         u = loc + 1;
>         urem = rem - (loc - ptr) - 1;
>         loc = memchr(u, *boundary, urem);
>     }
>     if (!loc) {
>         php_error(E_WARNING, "File Upload Error - No Mime boundary found after start of file header");
>         SAFE_RETURN;
>     }
> 
>     bytes = 0;
> 
>     fp = php_open_temporary_file(PG(upload_tmp_dir), "php", &temp_filename);
>     if (!fp) {
>         php_error(E_WARNING, "File upload error - unable to create a temporary file");
>         SAFE_RETURN;
>     }
>     if ((loc - ptr - 4) > PG(upload_max_filesize)) {
>         php_error(E_WARNING, "Max file size of %ld bytes exceeded - file [%s] not saved", PG(upload_max_filesize),namebuf);
>         upload_successful = 0;
>     } else if (max_file_size && ((loc - ptr - 4) > max_file_size)) {
>         php_error(E_WARNING, "Max file size exceeded - file [%s] not saved", namebuf);
>         upload_successful = 0;
>     } else if ((loc - ptr - 4) <= 0) {
>         upload_successful = 0;
> 
>     } else {
>         bytes = fwrite(ptr, 1, loc - ptr - 4, fp);
>         if (bytes < (loc - ptr - 4)) {
>     php_error(E_WARNING, "Only %d bytes were written, expected to write %ld", bytes, loc - ptr - 4);
>         }
>         upload_successful = 1;
>     }
>     fclose(fp);

これが原因なのであれば、php4への移行もやむなしかもしれません。
もしくは、tempfile生成処理をphp4に真似て修正するか。。。

良い解決方法がありますでしょうか?お教えください。

*********************************************
木村正人[kim]      E-Mail kim@ist.fujitsu.com+
(株)富士通インフォソフトテクノロジ 沼津事業所
       private Web http://www.izu.co.jp/~kim/
*********************************************