wecenter學習筆記-上傳圖片并生成縮略圖

該文是wecenter學習筆記的一部分

上傳圖片并生成縮略圖

這部分主要由兩個模塊合力完成

  • core_upload
  • core_image

上傳圖片

使用

AWS_APP::upload()->initialize(array(
    'allowed_types' => get_setting('allowed_upload_types'),
    'upload_path' => get_setting('upload_dir') . '/' . $item_type . '/' . gmdate('Ymd'),
    'is_image' => FALSE,
    'max_size' => get_setting('upload_size_limit')
));

...

AWS_APP::upload()->do_upload('qqfile');

...

if (AWS_APP::upload()->get_error())
{
    switch (AWS_APP::upload()->get_error())
    {
        default:
            H::ajax_json_output(AWS_APP::RSM(null, -1, AWS_APP::lang()->_t('錯誤代碼: '.AWS_APP::upload()->get_error())));
        break;

        case 'upload_invalid_filetype':
            H::ajax_json_output(AWS_APP::RSM(null, -1, AWS_APP::lang()->_t('文件類型無效')));
        break;

        case 'upload_invalid_filesize':
           H::ajax_json_output(AWS_APP::RSM(null, -1, AWS_APP::lang()->_t("文件尺寸過大,最大允許尺寸為 ".get_setting('upload_size_limit')." KB")));
        break;
    }
}

if (! $upload_data = AWS_APP::upload()->data())
{
    H::ajax_json_output(AWS_APP::RSM(null, -1, AWS_APP::lang()->_t( '上傳失敗, 請與管理員聯系' )));
}

步驟

  • 初始化(接受的文件類型、保存路徑、是否必須是圖片、最大文件尺寸)
  • 接受上傳文件
  • 錯誤處理

實現

core_upload處理文件上傳,涵蓋了

文件存儲和訪問暴露

先會將文件存儲到臨時文件中,在完成后面的檢查后再移動到目標目錄

文件類型判定并只允許上傳制定類型的文件(mimes)

根據上傳文件的擴展名判定是否是allowed_types,
如果是圖片文件,還會嘗試獲取圖片尺寸(利用獲取圖片大小會解析圖片頭部信息的副作用)
另外還是通過函數finfo_filefile命令或者mime_content_type來分析文件的mimes type

system/core/upload.php#file_mime_type

// We'll need this to validate the MIME info string (e.g. text/plain; charset=us-ascii)
   $regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/';

  1. 優先先會嘗試使用 `finfo_file`

    /* Fileinfo extension - most reliable method
     *
     * Unfortunately, prior to PHP 5.3 - it's only available as a PECL extension and the
     * more convenient FILEINFO_MIME_TYPE flag doesn't exist.
     */
    if (function_exists('finfo_file'))
    {
        $finfo = finfo_open(FILEINFO_MIME);
        if (is_resource($finfo)) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system
        {
            $mime = @finfo_file($finfo, $file['tmp_name']);
            finfo_close($finfo);
    
            /* According to the comments section of the PHP manual page,
             * it is possible that this function returns an empty string
             * for some files (e.g. if they don't exist in the magic MIME database)
             */
            if (is_string($mime) && preg_match($regexp, $mime, $matches))
            {
                $this->file_type = $matches[1];
                return;
            }
        }
    }
    
  2. 其次是file --brief --mime命令

    /* This is an ugly hack, but UNIX-type systems provide a "native" way to detect the file type,
     * which is still more secure than depending on the value of $_FILES[$field]['type'], and as it
     * was reported in issue #750 (https://github.com/EllisLab/CodeIgniter/issues/750) - it's better
     * than mime_content_type() as well, hence the attempts to try calling the command line with
     * three different functions.
     *
     * Notes:
     *  - the DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system
     *  - many system admins would disable the exec(), shell_exec(), popen() and similar functions
     *    due to security concerns, hence the function_exists() checks
     */
    if (DIRECTORY_SEPARATOR !== '\\')
    {
        $cmd = 'file --brief --mime ' . @escapeshellarg($file['tmp_name']) . ' 2>&1';
    
        if (function_exists('exec'))
        {
            /* This might look confusing, as $mime is being populated with all of the output when set in the second parameter.
             * However, we only neeed the last line, which is the actual return value of exec(), and as such - it overwrites
             * anything that could already be set for $mime previously. This effectively makes the second parameter a dummy
             * value, which is only put to allow us to get the return status code.
             */
            $mime = @exec($cmd, $mime, $return_status);
            if ($return_status === 0 && is_string($mime) && preg_match($regexp, $mime, $matches))
            {
                $this->file_type = $matches[1];
                return;
            }
        }
    
        if ( (bool) @ini_get('safe_mode') === FALSE && function_exists('shell_exec'))
        {
            $mime = @shell_exec($cmd);
            if (strlen($mime) > 0)
            {
                $mime = explode("\n", trim($mime));
                if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
                {
                    $this->file_type = $matches[1];
                    return;
                }
            }
        }
    
        if (function_exists('popen'))
        {
            $proc = @popen($cmd, 'r');
            if (is_resource($proc))
            {
                $mime = @fread($proc, 512);
                @pclose($proc);
                if ($mime !== FALSE)
                {
                    $mime = explode("\n", trim($mime));
                    if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
                    {
                        $this->file_type = $matches[1];
                        return;
                    }
                }
            }
        }
    }
    
  3. 然后 mime_content_type

    // Fall back to the deprecated mime_content_type(), if available (still better than $_FILES[$field]['type'])
    if (function_exists('mime_content_type'))
    {
        $this->file_type = @mime_content_type($file['tmp_name']);
        if (strlen($this->file_type) > 0) // It's possible that mime_content_type() returns FALSE or an empty string
        {
            return;
        }
    }
    
  4. 最后才會考慮用 getimagesize

    if (function_exists('getimagesize'))
    {
        $imageinfo = @getimagesize($file['tmp_name']);
    
        if ($imageinfo['mime'])
        {
            $this->file_type = $imageinfo['mime'];
    
            return;
        }
    }
    
    $this->file_type = $file['type'];
    

文件大小檢查

圖片尺寸檢查

文件名和文件內容安全校驗(xss clean)

會去掉文件名中的各類非法字符

system/core/upload.php#clean_file_name

    $bad = array(
                   "<!--",
                   "-->",
                   "'",
                   "<",
                   ">",
                   '"',
                   '&',
                   '$',
                   '=',
                   ';',
                   '?',
                   '/',
                   "%20",
                   "%22",
                   "%3c",      // <
                   "%253c",    // <
                   "%3e",      // >
                   "%0e",      // >
                   "%28",      // (
                   "%29",      // )
                   "%2528",    // (
                   "%26",      // &
                   "%24",      // $
                   "%3f",      // ?
                   "%3b",      // ;
                   "%3d"       // =
               );

   $filename = str_replace($bad, '', $filename);

** 文件內容的xss clean **

system/core/upload.php#do_xss_clean

if (function_exists('memory_get_usage') && memory_get_usage() && ini_get('memory_limit') != '')
{
    $current = ini_get('memory_limit') * 1024 * 1024;

    // There was a bug/behavioural change in PHP 5.2, where numbers over one million get output
    // into scientific notation.  number_format() ensures this number is an integer
    // http://bugs.php.net/bug.php?id=43053

    $new_memory = number_format(ceil(filesize($file) + $current), 0, '.', '');

    @ini_set('memory_limit', $new_memory); // When an integer is used, the value is measured in bytes. - PHP.net
}

// If the file being uploaded is an image, then we should have no problem with XSS attacks (in theory), but
// IE can be fooled into mime-type detecting a malformed image as an html file, thus executing an XSS attack on anyone
// using IE who looks at the image.  It does this by inspecting the first 255 bytes of an image.  To get around this
// CI will itself look at the first 255 bytes of an image to determine its relative safety.  This can save a lot of
// processor power and time if it is actually a clean image, as it will be in nearly all instances _except_ an
// attempted XSS attack.

if (function_exists('getimagesize') && @getimagesize($file) !== FALSE)
{
    if (($file = @fopen($file, 'rb')) === FALSE) // "b" to force binary
    {
        return FALSE; // Couldn't open the file, return FALSE
    }

    $opening_bytes = fread($file, 256);
    fclose($file);

    // These are known to throw IE into mime-type detection chaos
    // <a, <body, <head, <html, <img, <plaintext, <pre, <script, <table, <title
    // title is basically just in SVG, but we filter it anyhow

    if ( ! preg_match('/<(a|body|head|html|img|plaintext|pre|script|table|title)[\s>]/i', $opening_bytes))
    {
        return TRUE; // its an image, no "triggers" detected in the first 256 bytes, we're good
    }
    else
    {
        return FALSE;
    }
}

if (($data = @file_get_contents($file)) === FALSE)
{
    return FALSE;
}

return $data;

處理步驟

  1. 調整內存memory_limit為讀入文件保留足夠的內存
  2. 如果是圖片文件,要求文件不能有任何html標簽(a|body|head|html|img|plaintext|pre|script|table|title
  3. 如果不是圖片,只要不為空即可。

生成縮略圖算法

使用

AWS_APP::image()->initialize(array(
    'quality' => 90,
    'source_image' => $upload_data['full_path'],
    'new_image' => $thumb_file[$key],
    'width' => $val['w'],
    'height' => $val['h']
))->resize();

core_image負責縮略圖的生成

  • 生成指定大小的縮略圖(resize)
  • 可以選擇輸出到文件或者瀏覽器

resize的算法支持縮放和裁剪,并支持壓縮到一定清晰度

** 縮放 **

  • IMAGE_CORE_SC_NOT_KEEP_SCALE

    直接縮放或拉伸,不考慮比例

  • IMAGE_CORE_SC_BEST_RESIZE_WIDTH

    優先匹配縮放后的目標寬度

  • IMAGE_CORE_SC_BEST_RESIZE_HEIGHT

    高度優先匹配

** 裁剪 **

  • IMAGE_CORE_CM_DEFAULT

    不裁剪

  • IMAGE_CORE_CM_LEFT_OR_TOP

    對齊左上角,裁剪右下角

  • IMAGE_CORE_CM_MIDDLE

    居中對齊,裁剪四周

  • IMAGE_CORE_CM_RIGHT_OR_BOTTOM

    右下角對齊,裁剪左上角

實現

內部支持GDImageMagick來處理圖片,支持jpg/png/gif三種格式的圖片處理。

處理步驟:

  1. 根據圖片的寬窄比計算目標縮放區域(優先按長邊縮放)

    system/core/image.php#imageProcessImageMagick

    if ($this->source_image_w * $this->height > $this->source_image_h * $this->width)
    {
        $match_w = round($this->width * $this->source_image_h / $this->height);
        $match_h = $this->source_image_h;
    }
    else
    {
        $match_h = round($this->height * $this->source_image_w / $this->width);
        $match_w = $this->source_image_w;
    }
    
  2. 根據裁剪需求,設定目標區域

    system/core/image.php#imageProcessImageMagick

    switch ($this->clipping)
    {
        case IMAGE_CORE_CM_LEFT_OR_TOP:
            $this->source_image_x = 0;
            $this->source_image_y = 0;
        break;
    
        case IMAGE_CORE_CM_MIDDLE:
            $this->source_image_x = round(($this->source_image_w - $match_w) / 2);
            $this->source_image_y = round(($this->source_image_h - $match_h) / 2);
        break;
    
        case IMAGE_CORE_CM_RIGHT_OR_BOTTOM:
            $this->source_image_x = $this->source_image_w - $match_w;
            $this->source_image_y = $this->source_image_h - $match_h;
        break;
    }
    
    $this->source_image_w = $match_w;
    $this->source_image_h = $match_h;
    $this->source_image_x += $this->start_x;
    $this->source_image_y += $this->start_y;
    
  3. 根據縮放設置,計算出目標圖片的真實尺寸

    system/core/image.php#imageProcessImageMagick

    $resize_height = $this->height;
    $resize_width = $this->width;
    
    if ($this->scale != IMAGE_CORE_SC_NOT_KEEP_SCALE)
    {
        if ($this->scale == IMAGE_CORE_SC_BEST_RESIZE_WIDTH)
        {
            $resize_height = round($this->width * $this->source_image_h / $this->source_image_w);
            $resize_width = $this->width;
        }
        else if ($this->scale == IMAGE_CORE_SC_BEST_RESIZE_HEIGHT)
        {
            $resize_width = round($this->height * $this->source_image_w / $this->source_image_h);
            $resize_height = $this->height;
        }
    }
    
  4. 按目標區域裁剪圖片

  5. 縮放圖片到目標尺寸

  6. 輸出圖片

具體到圖片處理記得,根據庫的不同,略有不同

** imagemagick **

$im = new Imagick();

$im->readimageblob(file_get_contents($this->source_image));

$im->setCompressionQuality($this->quality);

if ($this->source_image_x OR $this->source_image_y)
{
    $im->cropImage($this->source_image_w, $this->source_image_h, $this->source_image_x, $this->source_image_y);
}

$im->thumbnailImage($resize_width, $resize_height, true);

if ($this->option == IMAGE_CORE_OP_TO_FILE AND $this->new_image)
{
    file_put_contents($this->new_image, $im->getimageblob());
}
else if ($this->option == IMAGE_CORE_OP_OUTPUT)
{
    $output = $im->getimageblob();
            $outputtype = $im->getFormat();

    header("Content-type: $outputtype");
    echo $output;
    die;
}

** gd **

通過 imagecopyresampledimagecopyresized可以一步作為裁剪和縮放

$func_resize($dst_img, $im, $dst_x, $dst_y, $this->source_image_x, $this->source_image_y, $fdst_w, $fdst_h, $this->source_image_w, $this->source_image_h);


表單防CSRF(Cross-site request forgery)的實現 ←o→ 對稱加密

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,836評論 18 139
  • 學習清單 這次10.1國慶節終于圓滿了自己一直以來的一個愿望,成為義工,放棄了所有的一切去到貴陽百花湖做12天的義...
    bb186d403078閱讀 143評論 0 0
  • 一、什么是requirejs 最早的時候,所有Javascript代碼都寫在一個文件里面,只要加載這一個文件就夠了...
    田田kyle閱讀 278評論 0 1
  • 文/安安 我想,未來的某一天,我們一定會再次見,事隔經年,我想跟你見面。 如果你愿意見我,我會站在你家小區外的馬路...
    安之以北閱讀 253評論 0 6
  • 五一小長假結束了,有點悵然若失的感覺,即使三天都泡在咖啡館似乎也沒有想象中那么充實,要學的東西實在實在太多,時間似...
    薛定諤貓貓閱讀 293評論 0 1