Do You PHP?    
Search Engine Optimization  php5 powerd  Valid XHTML 1.0!  Valid CSS!  このサイトのはてなブックマーク数 



last updated
2005/08/04

counter hits
since 1999/11/06


BLOB型の利用

alertここにある情報はかなり古くなっており、正しくなくなっている可能性があります。掲載しているサンプルコードiなどは、最新のPHPでは動作しない、もしくは、別途設定・調整が必要になるかも知れません。情報を鵜呑みにせず、あなたの手を動かして、あなたの目で確認してください。

Oracle8 から追加されたBLOB型の扱いについてまとめています。BLOB型を使えば、データベースに直接バイナリファイルを格納しておくことが可能です。なお、BLOBの詳細については、適宜Oracleのマニュアルを読んでください(^-^;

まず、INSERTの簡単にサンプルのフローを説明すると、以下のような感じです。

  • LOBロケータを定義する
  • 空のBLOBをINSERTして、BLOB型のカラムを返す
  • LOBロケータのsaveメソッドでファイルを格納する

注意点としては、以下の通りです。私は、2番目でハマりました(-"-;

  • BINDする前に、OCIDescriptorを実行し、LOBロケータに記憶領域を確保する
  • SQL実行時に、自動 commit させないこと。でないと、BLOBの格納時に、「ORA-22990: LOBロケータは複数のトランザクションにまたがることはできません」というエラーが発生する

ここでは、アップロードされたファイルをBLOB型のカラムにINSERTするサンプルを作ってみました。なお、以下のPHPスクリプトはPHP4.3.11+Oracle10g Release1の組み合わせで確認しています。また、ファイルアップロードの詳細については、PHPマニュアルの「ファイルアップロードの処理」を参照してください。

●テスト用テーブル定義:create_lob_tbl.sql
CREATE TABLE lob_tbl(
    filename VARCHAR2(100),
    filesize NUMBER(7),
    content_type VARCHAR2(100),
    binary BLOB
);
  
●lob_ins.php
  
<?php
    /**
     * テンポラリファイル読込関数
     */
    function loadfile($filename){
      $fp = fopen( $filename, 'r' );
      $buf = fread( $fp, filesize($filename) );
      fclose($fp);
      return $buf ;
    }

    if (!isset($_FILES['fname']) || $_FILES['fname']['error'] != UPLOAD_ERR_OK) {
?>
<form action='./lob_ins.php' method='post' enctype='multipart/form-data'>
ファイル名: <input type='file' name='fname'>
<input type='submit' value='アップロード'>
</form>
<?php
    }
    else {
        /**
         * upload されたファイルの情報を表示
         */
        $tmp_file_name = $_FILES['fname']['tmp_name'];
        $file_name = $_FILES['fname']['name'];
        $file_size = $_FILES['fname']['size'];
        $content_type = $_FILES['fname']['type'];
        echo 'テンポラリファイル名=' . $tmp_file_name . '<br>';
        echo '元のファイル名=' . $file_name . '<br>';
        echo 'サイズ=' . $file_size . '<br>';
        echo 'Content-type=' . $content_type . '<br>';

        $conn = OCILogon('scott', 'tiger', 'orcl');

        /**
         * LOBロケータの初期化
         */
        $lob = OCINewDescriptor($conn, OCI_D_LOB);

        /* SQL の作成 */
        $sql = 'INSERT INTO lob_tbl (filename, filesize, content_type, binary) '
             . 'VALUES(:fn, :fs, :ct, EMPTY_BLOB()) '
             . 'RETURNING binary INTO :bin ';

        $sql = OCIParse($conn, $sql);

        /**
         * パラメータのバインド
         */
        OCIBindByName($sql, ':FN', $file_name);
        OCIBindByName($sql, ':FS', $file_size);
        OCIBindByName($sql, ':CT', $content_type);
        OCIBindByName($sql, ':BIN', $lob, -1, OCI_B_BLOB);

        /**
         * トランザクションを切らさないために、
         * OCI_DEFAULT を指定
         */
        OCIExecute($sql, OCI_DEFAULT);

        /**
         * BLOBへの格納
         */
        if ($lob->save(loadfile($tmp_file_name))) {
            OCICommit($conn);
            echo 'アップロード成功<br>';
        }
        else {
            echo 'アップロード失敗<br>';
        }

        OCIFreeStatement($sql);
        OCILogoff($conn);
    }
?>

次に、INSERTしたデータを取り出すサンプルです。基本的には、通常のSELECTと変わりませんが、BLOB型データの出力の部分に特徴があります。モジュールは2つあり、テーブルのリストを作成する部分と、ダウンロードを実行する部分に分かれています。ダウンロードする部分では、以下のようなフローになっています。

  • データをFETCH
  • HeaderでContent-typeを出力
  • loadメソッドを使ってBLOB型データをLoadし、そのままechoで出力

なお、パラメータとしてOracleの行ID(ROWID)を渡していますが、ROWIDは「+」を含む可能性があるのでrawurlencode関数を使用してエンコードして渡すようにしています。

●lob_sel.php
  
<?php
    $conn = OCILogon("scott", "tiger", "orcl");

    /**
     * SQLの作成
     */
    $sql = "SELECT ROWIDTOCHAR(ROWID) rid, filename, filesize "
         . "FROM lob_tbl ";

    $sql = OCIParse($conn, $sql);
    OCIExecute($sql, OCI_DEFAULT);
?>
<table border=1>
<tr>
<th>No.</th>
<th>ファイル名</th>
<th>サイズ</th>
</tr>
<?php
    $cnt = 0;
    while(OCIFetchInto($sql, $arr, OCI_ASSOC)){
        $cnt++;
?>
<tr>
<th>
<a href="lob_sel2.php?rid=<?php echo rawurlencode($arr["RID"]); ?>">
<?php echo $cnt; ?>
</a></th>
<td><?php echo $arr["FILENAME"]; ?></td>
<td><?php echo $arr["FILESIZE"]; ?></td>
</tr>
<?php
    }
?>
</table>
<?php
    OCIFreeStatement($sql);
    OCILogoff($conn);
?>

●lob_sel2.php
  
<?php
    if (!isset($_GET['rid'])) {
        die('invalid parameter');
    }

    $rid = $_GET['rid'];
    $conn = OCILogon('scott', 'tiger', 'orcl');

    /**
     * SQLの作成
     */
    $sql  = 'SELECT filename, content_type, binary '
          . 'FROM lob_tbl '
          . 'WHERE ROWID = ROWIDTOCHAR(:rid) ';

    $stmt = OCIParse($conn, $sql);
    OCIBindByName($stmt, ':RID', $rid);
    OCIExecute($stmt, OCI_DEFAULT);
    OCIFetchInto($stmt, $arr, OCI_ASSOC);

    $filename = $arr['FILENAME'];
    $content_type = $arr['CONTENT_TYPE'];
    $data = $arr['BINARY'];

    /**
     * 日本語ファイル名の場合、Internet Explorerだとsjisにしてやる
     * 必要があるらしい。
     *
     * Firefoxの場合、出力エンコーディングと同じにする。
     *
     * @see http://ns1.php.gr.jp/pipermail/php-users/2002-July/008509.html
     */
    if (ereg(' MSIE ', $_SERVER['HTTP_USER_AGENT'])) {
        $encoding = 'sjis';
    }
    else {
        $encoding = mb_http_output();    // Firefox
    }
    $filename = mb_convert_encoding(
                    $filename,
                    $encoding,
                    mb_internal_encoding());

    /**
     * 出力変換を解除し、ヘッダを出力
     */
    mb_http_output('pass');
    header('Content-Type: ' . $content_type);
    header('Content-Disposition: attachment; filename=' . $filename);
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0,pre-check=0');
    header('Pragma: public');

    /**
     * ファイル本体の出力
     */
    echo $data->load();

    OCIFreeStatement($stmt);
    OCILogoff($conn);
?>



About This Site |  Privacy Policy |  Contact
Copyright © 1999 - 2005 by Hideyuki SHIMOOKA all rights reserved.