2016/01/08

Preview Image Before Upload

後台上傳圖片時,若想要看到預覽圖片,早期只有 IE 有 function 可以吃到 localhost 的檔案,讓你看到你要上傳的圖片樣貌,現在 HTML5 有 FileReader 的 api 可以用,範例如下:

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js"></script>
    <script type="text/javascript" charset="utf-8">
    $(function() {
        var allows = ['jpg', 'jpeg', 'gif', 'png'];

        $('#file').on('change', function() {
            var reader = new FileReader();
            var ext = $(this).val().split('.').pop().toLowerCase();

            try {
                if ($.inArray(ext, allows) === -1) {
                    throw new Error('檔案格式錯誤,僅允許' + allows.join(', '));
                }

                reader.readAsDataURL(this.files[0]);
                reader.onload = function(e) {
                    $('#content').html('<img src="' + e.target.result + '">');
                }
            } catch (e) {
                alert(e.message);
            }
        });
    });
    </script>
</head>
<body>
<input id="file" type="file" name="file">
<div id="content"></div>
</body>
</html>

這個方法有幾個問題:

  1. 瀏覽器支援問題
  2. 真實呈現問題(假設我們有透過後端處裡圖片無法呈現最後樣貌)

基於以上兩點,我決定還是透過 ajax + php 的方法來做掉,我的方法是,檔案 onChange 的時候 upload 去 server 端,請 server 端處理 tmp_name,然後傳回 base64_encode 的結果,這樣的好處是檔案我沒有使用 move 搬移,server 會自動幫我定期清除那些暫存檔,使用 base64 編碼因此不需要衍生其他檔案,做法如下:

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.form/3.51/jquery.form.min.js"></script>
    <script type="text/javascript" charset="utf-8">
    $(function() {
        $('#file').on('change', function() {
            $('#frm').ajaxSubmit({
                url: 'preview.php',
                success: function(response) {
                    if (response.status !== 'done') {
                        alert(response.message);
                    } else {
                        $('#content').html('<img src="' + response.message + '">');
                    }
                }
            });

            return false;
        });
    });
    </script>
</head>
<body>
<form id="frm" method="post" action="upload.php">
<input id="file" type="file" name="file">
</form>
<div id="content"></div>
</body>
</html>
php
<?php

header('Content-Type: application/json');

include 'vendor/autoload.php';

$status = 'done';
$message = '';
$allows = ['jpeg', 'png', 'gif'];
$file = $_FILES['file'];
$map = function($item) {
    return "image/{$item}";
};

try {
    if ($file['name'] === '') {
        throw new Exception("請選擇檔案");
    }

    $type = $file['type'];

    if (!in_array($type, array_map($map, $allows))) {
        throw new Exception('檔案格式錯誤,僅接受'.implode(', ', $allows));
    }

    $layer = PHPImageWorkshop\ImageWorkshop::initFromPath($file['tmp_name']);
    $layer->resizeInPixel(100, null, true, 0, 0, 'MM');
    $result = $layer->getResult();

    ob_start();
    call_user_func(str_replace('/', '', $type), $result);
    $image = ob_get_contents();
    ob_end_clean();

    $message = "data:{$type};base64,".base64_encode($image);
} catch (Exception $e) {
    $status = 'fail';
    $message = $e->getMessage();
}

echo json_encode(compact('status', 'message'));

我使用了 ImageWorkshop 來處理縮圖等等的功能,這個部分可以自行替換 solution。

沒有留言: