홈페이지 제작을 하다보면 ,php에서 엑셀파일인 xsl 로 다운받는 기능이 필요할때가 많다. 특히 관공서홈페이지를 제작하다보면 , 필수 항목이다. 기존에 하던 방식은 해더를 붙인후에 , table 태그를 이용하는 방식이었다. 이 방식이 잘 먹히긴 했는데, 익스플로가 7로 업그래이드 되면서 먹히지 않는 경우가 발생했다. 난감해 하며 여기저기 방법을 찾다가 아래 CLASS를 발견했다.

<<MS-Excel Stream Handler (http://www.phpclasses.org/browse/package/1919.html) >>라는 이름을 가진 멋진 클래스다.

아래는 간단한 사용예제다. 원하는 내용을 배열로 만들기만 하면 , 엑셀파일로 다운로드가 된다. 따로 저장할수도 있고, 바로 다운로드도 가능하다.

 

<?php

/**

* MS-Excel stream handler

* Excel export example

* @author      Ignatius Teo            <ignatius@act28.com>

* @copyright   (C)2004 act28.com       <http://act28.com>

* @date        21 Oct 2004

*/

require_once "excel.php";

$export_file = "xlsfile://example.xls";//저장하고싶은 파일이름, 앞에 꼭 xlsfile:// 를 붙여야한다.

$fp = fopen($export_file, "wb");

if (!is_resource($fp))

{

    die("Cannot open $export_file");

}

// 엑셀에 들어갈 내용을 배열로 만든다.

$assoc = array(

    array("Sales Person" => "엑셀된다 만만세", "Q1" => "$3255", "Q2" => "$3167", "Q3" => 3245, "Q4" => 3943),

    array("Sales Person" => "Jim Brown", "Q1" => "$2580", "Q2" => "$2677", "Q3" => 3225, "Q4" => 3410),

    array("Sales Person" => "John Hancock", "Q1" => "$9367", "Q2" => "$9875", "Q3" => 9544, "Q4" => 10255),

);

fwrite($fp, serialize($assoc));

fclose($fp);

//엑셀파일로 다운로드

$export_file = "xlsfile://example.xls";//다운로드할 파일 이름

header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT");

header ("Last-Modified: " . gmdate("D,d M YH:i:s") . " GMT");

header ("Cache-Control: no-cache, must-revalidate");

header ("Pragma: no-cache");

header ("Content-type: application/x-msexcel");

header ("Content-Disposition: attachment; filename=\"" . basename($export_file) . "\"" );

header ("Content-Description: PHP/INTERBASE Generated Data" );

readfile($export_file);

exit;

?>

 

엑셀 클래스 파일 

아래내용을 excel.php 라는 이름으로 저장하면 된다.

 

<?php
/**
* MS-Excel stream handler
* This class read/writes a data stream directly
* from/to a Microsoft Excel spreadsheet
* opened with the xlsfile:// protocol
* This is used to export associative array data directly to MS-Excel
* @requires    PHP 4 >= 4.3.2
* @author      Ignatius Teo            <ignatius@act28.com>
* @copyright   (C)2004 act28.com       <http://act28.com>
* @version     0.3
* @date        20 Jan 2005
* $Id: excel.php,v 1.3 2005/01/20 09:58:58 Owner Exp $
*/
class xlsStream
{
    /* private */
    var $position = 0;          // stream pointer
    var $mode = "rb";           // default stream open mode
    var $xlsfilename = null;    // stream name
    var $fp = null;             // internal stream pointer to physical file
    var $buffer = null;         // internal write buffer
    var $endian = "unknown";    // little | unknown | big endian mode
    var $bin = array(
        "big" => "v",
        "little" => "s",
        "unknown" => "s",
    );

    /**
     * detect server endian mode
     * thanks to Charles Turner for picking this one up
     * @access    private
     * @params    void
     * @returns    void
     * @see        http://www.phpdig.net/ref/rn45re877.html
     */
    function _detect()
    {
        // A hex number that may represent 'abyz'
        $abyz = 0x6162797A;

        // Convert $abyz to a binary string containing 32 bits
        // Do the conversion the way that the system architecture wants to
        switch (pack ('L', $abyz))
        {
            // Compare the value to the same value converted in a Little-Endian fashion
            case pack ('V', $abyz):
                $this->endian = "little";
                break;

            // Compare the value to the same value converted in a Big-Endian fashion
            case pack ('N', $abyz):
                $this->endian = "big";
                break;

            default:
                $this->endian = "unknown";
                break;
        }
    }

    /**
     * called by fopen() to the stream
     * @param   (string)    $path           file path
     * @param   (string)    $mode           stream open mode
     * @param   (int)       $options        stream options (STREAM_USE_PATH |
     *                                      STREAM_REPORT_ERRORS)
     * @param   (string)    $opened_path    stream opened path
     */
    function stream_open($path, $mode, $options, &$opened_path)
    {
        $url = parse_url($path);
        $this->xlsfilename = '/' . $url['host'] . $url['path'];
        $this->position = 0;
        $this->mode = $mode;

        $this->_detect();    // detect endian mode

        //@TODO: test for invalid mode and trigger error if required

        // open underlying resource
        $this->fp = @fopen($this->xlsfilename, $this->mode);
        if (is_resource($this->fp))
        {
            // empty the buffer
            $this->buffer = "";

            if (preg_match("/^w|x/", $this->mode))
            {
                // write an Excel stream header
                $str = pack(str_repeat($this->bin[$this->endian], 6), 0x809, 0x8, 0x0, 0x10, 0x0, 0x0);
                fwrite($this->fp, $str);
                $opened_path = $this->xlsfilename;
                $this->position = strlen($str);
            }
        }
        return is_resource($this->fp);
    }

    /**
     * read the underlying stream resource (automatically called by fread/fgets)
     * @todo    modify this to convert an excel stream to an array
     * @param   (int)       $byte_count     number of bytes to read (in 8192 byte blocks)
     */
    function stream_read($byte_count)
    {
        if (is_resource($this->fp) && !feof($this->fp))
        {
            $data .= fread($this->fp, $byte_count);
            $this->position = strlen($data);
        }
        return $data;
    }

    /**
     * called automatically by an fwrite() to the stream
     * @param   (string)    $data           serialized array data string
     *                                      representing a tabular worksheet
     */
    function stream_write($data)
    {
        // buffer the data
        $this->buffer .= $data;
        $bufsize = strlen($data);
        return $bufsize;
    }

    /**
     * pseudo write function to manipulate the data
     * stream before writing it
     * modify this to suit your data array
     * @access  private
     * @param   (array)     $data           associative array representing
     *                                      a tabular worksheet
     */
    function _xls_stream_write($data)
    {
        if (is_array($data) && !empty($data))
        {
            $row = 0;
            foreach (array_values($data) as $_data)
            {
                if (is_array($_data) && !empty($_data))
                {
                    if ($row == 0)
                    {
                        // write the column headers
                        foreach (array_keys($_data) as $col => $val)
                        {
                            // next line intentionally commented out
                            // since we don't want a warning about the
                            // extra bytes written
                            // $size += $this->write($row, $col, $val);
                            $this->_xlsWriteCell($row, $col, $val);
                        }
                        $row++;
                    }

                    foreach (array_values($_data) as $col => $val)
                    {
                        $size += $this->_xlsWriteCell($row, $col, $val);
                    }
                    $row++;
                }
            }
        }
        return $size;
    }

    /**
     * Excel worksheet cell insertion
     * (single-worksheet supported only)
     * @access  private
     * @param   (int)       $row            worksheet row number (0...65536)
     * @param   (int)       $col            worksheet column number (0..255)
     * @param   (mixed)     $val            worksheet row number
     */
    function _xlsWriteCell($row, $col, $val)
    {
        if (is_float($val) || is_int($val))
        {
            // doubles, floats, integers
            $str  = pack(str_repeat($this->bin[$this->endian], 5), 0x203, 14, $row, $col, 0x0);
            $str .= pack("d", $val);
        }
        else
        {
            // everything else is treated as a string
            $l    = strlen($val);
            $str  = pack(str_repeat($this->bin[$this->endian], 6), 0x204, 8 + $l, $row, $col, 0x0, $l);
            $str .= $val;
        }
        fwrite($this->fp, $str);
        $this->position += strlen($str);
        return strlen($str);
    }

    /**
     * called by an fclose() on the stream
     */
    function stream_close()
    {
        if (preg_match("/^w|x/", $this->mode))
        {
            // flush the buffer
            $bufsize = $this->_xls_stream_write(unserialize($this->buffer));

            // ...and empty it
            $this->buffer = null;

            // write the xls EOF
            $str = pack(str_repeat($this->bin[$this->endian], 2), 0x0A, 0x00);
            $this->position += strlen($str);
            fwrite($this->fp, $str);
        }

        // ...and close the internal stream
        return fclose($this->fp);
    }

    function stream_eof()
    {
        $eof = true;
        if (is_resource($this->fp))
        {
            $eof = feof($this->fp);
        }
        return $eof;
    }
}

stream_wrapper_register("xlsfile", "xlsStream")
    or die("Failed to register protocol: xlsfile");
?>

Posted by 달을파는아이 달을파는아이

트랙백 보낼 주소 :: http://moonseller.net/trackback/150 관련글 쓰기

댓글을 달아주세요:: 네티켓은 기본, 스팸은 사절

  1. 2009/02/12 12:55
    댓글 주소 수정/삭제 댓글
    와 이런 코드 있으면 좋겠다 생각했는데, 여기 있었군요. 좋은 정보 잘 보았습니다. 나중에 한 번 써먹어야겠네요. 감사합니다.
    • 2009/02/13 09:35
      댓글 주소 수정/삭제
      ^^ 좋은일에 쓰세요~ ㅋㅋ
  2. 2009/07/24 19:22
    댓글 주소 수정/삭제 댓글
    감사합니다~ ^^
    그런데, 오류가 하나 떠서요.. 왜 그런지 모르겠어요..
    혹시 나중에라도 보시면 좀 조언 부탁드립니다..

    ---------------------------------------
    Warning: fopen(xlsfile://example.xls): failed to open stream: "xlsstream::stream_open" call failed in /home/page_excel.php on line 21
    Cannot open xlsfile://example.xls

    ---------------------------------------
    17 require_once "excel.php";
    18
    19 $export_file = "xlsfile://example.xls";//저장하고싶은 파일이름, 앞에 꼭 xlsfile:// 를 붙여야한다.
    20
    21 $fp = fopen($export_file, "wb";);
    22
    23 if (!is_resource($fp))
    24 {
    25 die("Cannot open $export_file";);
    26 }
    • 2010/03/25 09:43
      댓글 주소 수정/삭제
      말그대로 그 위치에 example.xls 라는 파일이 없어서 그렇습니다.
      경로가 정확한지 한번 체크해 보실래요?
  3. 2009/07/24 20:37
    댓글 주소 수정/삭제 댓글
    혹시.. 리눅스 서버에서는 안되는건가요??
    아무리 찾아도 모르겠네요..
  4. 하늘치
    2009/07/26 07:12
    댓글 주소 수정/삭제 댓글
    아.. 잘 됩니다!
    example.xls 가 저장될 디렉토리에 쓰기 권한이 없어서 그랬던 거였어요.
    tmp 폴더를 만들어서 쓰기 권한 주고

    $export_file = "xlsfile://tmp/example.xls";

    라고 하니까 되네요. ^^
  5. 제이한
    2010/03/25 09:34
    댓글 주소 수정/삭제 댓글
    한글이 깨지는데 혹시 원인을 아세요??
    • 2010/03/25 09:41
      댓글 주소 수정/삭제
      한글이 깨지나요?
      인코딩쪽을 한번 맞춰보세요. 클래스도 utf8로 하고 실행되는 파일도 그렇게 맞춰보시면 한글이 안깨질거같은데..
  6. 잠만보
    2010/06/07 12:02
    댓글 주소 수정/삭제 댓글
    저는 excel_test.php를 다운로드할 수 없습니다.
    라고 윈도우즈 경고창이 뜨는데 이건 왜 그런지 혹시 아시나요???
  7. 2011/10/27 13:48
    댓글 주소 수정/삭제 댓글
    안되는 경우
    1. 엑셀파일/경로에 쓰기권한이 없을때
    2. 경로폴더 대문자가 쓰였을때
    3. 익스 말고 기타 브라우져일때 캐쉬 읽어드릴때(수정내용 반영안되서)
    - 익스는
    도구->인터넷옵션->[일반] [검색기록] [설정]->저장된페이지의 새 버전확인: 페이지를 열때마다
  8. 아로
    2012/01/25 21:44
    댓글 주소 수정/삭제 댓글
    Warning: Cannot modify header information - headers already sent by (output started at .\excelExample.php:2) in .\excelExample.php on line 52

    header 사용한 모든 곳에 위 에러가 뜨네요. 포럼에 가보니 PHP5 에서는 작동안된다는 얘기도 있고ㅠㅠ (제가 PHP5.2.17 인데 ㅠㅠ)

    waring 나오고 마지막에는 화면에
    Sales Person Q1 Q2 Q3 Q4엑셀된다 만만세 $3255 $3167Z�@冠@ Jim Brown $2580 $26772�@ㄺ@ John Hancock $9367 $9875ㅒ@���@
    한글 깨져서 나옵니다. 어케 된 걸까요?


BLOG main image
Every day is a new life to a wise man by 달을파는아이

나의 인생 시계 만들기 >>

카테고리

분류 전체보기 (284)
나름수익모델연구 (75)
웹이야기 (61)
달을파는아이의 선택 (122)
책읽은자랑 (26)
여행 (0)
Statistics Graph