2016/11/01

Codeigniter file_manager helper

檔案上傳是蠻常使用的功能,ci 裡面有內建檔案上傳的功能,但我們當然不想記得繁複的呼叫方式,寫成 helper 來使用是最方便的,今天 news 當作範例示範一下寫法。

news table
CREATE TABLE `news` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(50) NOT NULL,
`file_name` VARCHAR(100) NOT NULL,
`real_name` VARCHAR(100) NOT NULL
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci;
application/controllers/News.php
class News extends CI_Controller
{
    public function index()
    {
        $this->load->helper('form');
        $this->load->view('news');
    }

    public function save()
    {
        $this->load->database();
        $this->load->helper('file_manager');
        $data = [];

        if (has_upload('file')) {
            $upload = file_upload('file', 'assets/uploads/news');

            if ($upload['status'] == 'fail') {
                die($upload['message']);
            } else {
                $data['file_name'] = $upload['fileName'];
                $data['real_name'] = $upload['realName'];
            }
        }

        $data['name'] = uniqid();
        $this->db->insert('news', $data);
    }

    public function download($id)
    {
        $this->load->database();
        $this->load->helper('file_manager');
        $row = $this->db->get_where('news', ['id' => $id])->row_array();

        force_download("assets/uploads/news/{$row['file_name']}", $row['real_name']);
    }
}

index method 就是簡單的建立一個表單,傳送 name 跟 file 這兩的 field 到 save 儲存,這邊做的都是最簡單的示範,實際情況還是資照需求去修改,但回傳的內容應該都可以應付其他的條件,download 的部份就是調用 file_manager helper 下載原始的檔案,若非必要,盡量不要在 helper 裡面做 db connection,因為 helper 很有可能會在迴圈裡面出現,這樣 db query 數量會隨資料量增加。

application/views/news.php
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>News</title>
    </head>
    <body>
        <?php echo form_open_multipart('news/save'); ?>
        <?php echo form_input('name', uniqid()); ?>
        <?php echo form_upload('file'); ?>
        <?php echo form_submit('go', 'go'); ?>
        <?php echo form_close(); ?>
    </body>
</html>

news 的 view,簡單的使用 form helper 生成。

application/helpers/file_manager_helper.php
if (!function_exists('file_upload')) {
    function file_upload($file, $path, array $allows = [])
    {
        $ci =& get_instance();
        $status = 'done';
        $message = '';
        $fileName = '';
        $realName = '';
        $config['upload_path'] = $path;
        $config['allowed_types'] = (count($allows) > 0) ? implode('|', $allows) : '*';
        $config['encrypt_name'] = true;
        $ci->load->library('upload', $config);

        if (!$ci->upload->do_upload($file)) {
            $message = $ci->upload->display_errors();
            $status = 'fail';
        } else {
            $fileData = $ci->upload->data();
            $fileName = $fileData['file_name'];
            $realName = $fileData['client_name'];
        }

        return compact('status', 'message', 'fileName', 'realName');
    }
}

if (!function_exists('force_download')) {
    function force_download($path, $realName)
    {
        $originFile = urlencode($realName);
        $realFile = $path;

        header("Pragma: public");
        header("Expires: 0");
        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header("Content-Type: application/force-download");
        header("Content-Disposition: attachment; filename=".$originFile);
        header("Content-Description: File Transfer");
        @readfile($realFile);
    }
}

if (!function_exists('has_upload')) {
    function has_upload($file)
    {
        if (isset($_FILES[$file]) && is_uploaded_file($_FILES[$file]['tmp_name'])) {
            return true;
        }

        return false;
    }
}

file_upload function 調用了 upload 的 library,因為主要是傳檔案,所以預設 allows 為全部,當然也有先寫好參數可以傳遞,印象中 codeigniter 的上傳會檢查 mime,所以 allow 的內容可能要符合 mime 的檔名規範,如果要上傳的是圖片的東西,可以用這個 function 去改出一個 image_upload 的 function,再把跟圖片比較有關連的內容放進去,在上傳的時候我們會將檔名使用英文編碼代替,但原始的檔名會一起存到 database,在很多 Linux server 檔案名稱使用非英文的話會無法讀取,所以最好是用純英數去儲存檔案,因為 codeigniter 的 input helper 本身沒有支援 file 的內容,所以寫了一個 has_upload 的 function 來檢查這次的傳輸有沒有上傳檔案。

force_download 這個 function 只要將檔案位置跟原始檔名帶入,便可以下載原始的檔案,即便是中文也沒問題。

沒有留言: