照片處理

在瀏覽器上傳圖片與顯示


上傳照片與顯示比我想像中的複雜許多啊!之前主要都是處理簡單的json file來往,如今要處理檔案就沒這麼輕鬆了。
觀念與實作上有兩個部分要釐清:

  1. HTML5新支援的File與Blob API
    HTML5增進了許多出色的功能,與檔案相關的部分就是上述兩個API,檔案可以透過`<input type="file">向Local端索取資料,抓到後可以暫存在瀏覽器記憶體中。
    Blob是一種類似檔案的資料暫存方式,而File則是基於Blob而擴充的功能,使用這兩個API在調用檔案部分就更加的方便了,不過處理檔案還有諸多的編碼方式,其中的轉換我也看得霧裡來霧裡去OTZ,所以就不再多家著墨。 詳見MDN文檔黑暗執行緒html5rock
  2. 如何預覽並裁剪圖片
    我使用的是Croppie.js,功能簡單又好用,不過官方文件寫的略微簡陋,建議到他的github直接看源碼,copy and modify最實在。 Crop最後回傳的resp打印出來是base64編碼的格式,我直接用json格式封裝傳送。 以下是部分代碼
    $uploadCrop.croppie('result', {
         type: 'canvas',
         size: 'viewport'
     }).then(function (resp) {
         $uploadCrop.fadeOut("slow",function(){
             $('#personalPorfile').attr('src',resp).fadeIn('slow');
             $('#importBtn').fadeIn('fast');
             $('#cropBtn').fadeOut('fast');
         });
         console.log(resp);
         $.ajax({
             type: "POST",
             url: myURL + "/users/profile",
             data: {profile:resp},
             success: function(data){
                 console.log(data);
             },
             error: function(data){
                 console.log(data);
             }
         });
     });
    

    上傳至Server並儲存

    如何上傳至Server也花了我不少時間才實做出來,主要有兩個部分要釐清:
  3. 瀏覽器端的編碼選擇:在stackflow上有討論 為何二進制文件需要編碼傳輸,內容提到主要是因為有些protocol會誤認為二進制文件是執行的指令,所以編碼後傳輸就不會出問題
    a. 使用POST and encrypted by multipart/form-data:
    POST的Content-type主要決定內文主體的編碼方式,用法可參考POST encrypted機制,這是標準的上傳文件的方式,如果想在瀏覽器使用AJAX需要透過FormData的方式包裝資料(詳見MDN解說)在Node JS Server部 分可以用Express組織寫的multer模組。
    b. 使用Base64編碼:
    這也是我目前採用的方式,主要原因是我採用croppie.js裁剪圖片後回回傳base64格式的圖片,後來就乾脆直接以base64方式傳輸過去,之後NodeJS解析後就直接儲存起來。參考資料
  4. 如何轉碼與儲存檔案
    上述兩種方式我都有試過,第一種的話我是用multer,很簡單就可以儲存檔案,而且有許多方便的擴展功能(etc. 命名檔案與儲存路徑等),客戶端推薦用PostMan先做測試。 第二種方式也蠻簡單的,就是透過轉碼在用fs模組把檔案存起來,以下代碼也是參考stackoverflow的解答。

    router.post('/profile',function (req, res, next) {
     console.log("upload image here");
     console.log(req.authUser.account);
    
     var base64Data = req.body.profile.replace(/^data:image\/png;base64,/, "");
    
     fs.writeFile("public/uploads/"+ req.authUser.account +".png", base64Data, 'base64', function(err) {
         if(err){
             res.status(401).json("error");
         }else{
             res.status(201).json("success");
         }
     });
    })