天天看點

混合APP開發之5+API上傳圖檔過大導緻上傳失敗的解決方式

場景:需要拍照或從相冊選取多張圖檔上傳。

遇到問題:5+API中plus.uploader管理對象完成上傳功能後發現如果上傳圖檔大于1000kb,圖檔上傳速度減慢,圖檔大于2M則上傳失敗,但接口傳回結果狀态為200,也就是上傳成功。現需要壓縮圖檔上傳。

接口位址:

https://www.html5plus.org/doc/zh_cn/uploader.html#plus.uploader.Upload

API坑:

在plus.camera管理攝像頭的對象中,supportedImageResolutions屬性( 字元串數組,攝像頭支援的拍照分辨率) 能夠擷取手機硬體得所有分辨率參數,傳回字元串數組。但是該屬性無法設定攝像頭使用的分辨率,故而設定無效,拍出來的照片永遠是你手機最大分辨率。這就導緻了拍出來的照片一般都是2-3M居多

官方給的例子中設定了該參數:

supportedImageResolutions

字元串數組,攝像頭支援的拍照分辨率

說明:

Array 類型 隻讀屬性

屬性類型為String[],若不支援此屬性則傳回空數組對象。攝像頭支援的拍照圖檔分辨率字元串形式“WIDTH*Height”,如“400*800”;如果支援任意自定義分辨率則“*”。

平台支援:

Android (支援)
iOS (不支援): 傳回空數組對象
示例:


<!DOCTYPE html>
<html>
    <head>
    <meta charset="utf-8">
    <title>Camera Example</title>
    <script type="text/javascript">
// 擴充API加載完畢後調用onPlusReady回調函數 
document.addEventListener( "plusready", onPlusReady, false );
// 擴充API加載完畢,現在可以正常調用擴充API 
function onPlusReady() {
    var cmr = plus.camera.getCamera();
    alert( "Camera supperted image resolutions: " + cmr.supportedImageResolutions );//無效
}
    </script>
    </head>
    <body>
           

上例中官方給出的是添加的resolution屬性,然而在官方文檔中的CameraOption卻沒有這個屬性,如下

CameraOption

JSON對象,調用攝像頭的參數


interface CameraOption {
    attribute String filename;
    attribute String format;
    attribute String index;
    attribute PopPosition popover;
}

屬性:

filename: (String 類型 )拍照或攝像檔案儲存的路徑
可設定具體檔案名(如"_doc/camera/a.jpg");也可隻設定路徑,以"/"結尾則表明是路徑(如"_doc/camera/")。如未設定檔案名稱或設定的檔案名沖突則檔案名由程式程式自動生成。

format: (String 類型 )拍照或攝像的檔案格式
可通過Camera對象的supportedImageFormats或supportedVideoFormats擷取,如果設定的參數無效則使用系統預設值。

index: (String 類型 )拍照或攝像預設使用的攝像頭
拍照或攝像界面預設使用的攝像頭編号,表示主攝像頭,表示輔攝像頭。

平台支援
Android - + (不支援): 暫不支援設定攝像頭,忽略此屬性值
iOS - + (支援)
popover: (PopPosition 類型 )拍照或攝像界面彈出訓示區域
對于大螢幕裝置如iPad,拍照或攝像界面為彈出視窗,此時可通過此參數設定彈出視窗位置,其為JSON對象,格式如{top:"10px",left:"10px",width:"200px",height:"200px"},預設彈出位置為螢幕居中。

平台支援
Android - ALL (不支援): 暫不支援設定攝像頭,忽略此屬性值
iOS - + (支援): 僅iPad裝置支援此屬性,iPhone/iTouch上忽略此屬性值
           

是以這是第一個,這樣我們就無法通過控制攝像頭的分辨率來保證拍照擷取的照片都比較小。

是以,希望通過壓縮圖像處理,檢視官方文檔(此處為API第二個坑)

compressImage,//該接口是plus.zip.compressImage

圖檔壓縮轉換


void plus.zip.compressImage( options, successCB, errorCB);

說明:

可用于圖檔的品質壓縮、大小縮放、方向旋轉、區域裁剪、格式轉換等。

參數:

options: ( CompressImageOptions ) 必選 
圖檔壓縮轉換的參數
successCB: ( CompressImageSuccessCallback ) 可選 
圖檔壓縮轉換操作成功回調,操作成功時調用。
errorCB: ( ZipErrorCallback ) 可選 
圖檔壓縮轉換操作失敗回調,操作失敗時調用。
           

以上親測無效,提示找不到這個Function,在官方提供的問答社群中資料較少,有人也表示找不到這個方法,但也有人問 壓縮中出現的問題,不過年代久已,是15年的問題,至今好像都沒處理,問答社群不夠活躍。

經過查詢相關資料,最終使用canvas來壓縮處理圖像。以下是使用方法:
//添加img和canvas标簽,一個用于存放原圖檔,一個用于drawImage并擷取壓縮後的資料
<canvas id="canvas" style="display: none;"></canvas>
<img id="source" src="" style="display: none;"> 


function gallery_get_base64(files)//參數:原圖檔的位址[數組],該位址可以通過gallery.pick傳回或camera.captureImage中的success回調函數獲得
                {
                    var cur_file=files.pop();//注意*壓縮過程中,因為傳入的是圖檔路徑數組,必須用遞歸方式去壓縮,否則如果通過for循環的話,還未壓縮完成一張,for循環已經結束,最終隻會得到最後一張圖檔
                plus.nativeUI.showWaiting('加載中');
                var canvas,source,dataUrl;  


                     canvas = document.getElementById('canvas');
 source = document.getElementById('source');
 dataUrl;//存儲畫在畫布上的資料
source.onload=function(){//當原圖檔加載好時觸發

    var width = source.width;
    var height = source.height;
    var context = canvas.getContext('2d');

    // draw image params
    var sx = ;
    var sy = ;
    var sWidth = width;
    var sHeight = height;
    var dx = ;
    var dy = ;
    var dWidth = width;
    var dHeight = height;
    var quality = ;//圖檔的品質範圍為0-1,設定成0.2的話3M圖檔大概能壓縮成250-290k左右

    canvas.width = width;
    canvas.height = height;

/*
剪切圖像,并在畫布上定位被剪切的部分:
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);


參數  描述
img 規定要使用的圖像、畫布或視訊。
sx  可選。開始剪切的 x 坐标位置。
sy  可選。開始剪切的 y 坐标位置。
swidth  可選。被剪切圖像的寬度。
sheight 可選。被剪切圖像的高度。
x   在畫布上放置圖像的 x 坐标位置。
y   在畫布上放置圖像的 y 坐标位置。
width   可選。要使用的圖像的寬度。(伸展或縮小圖像)
height  可選。要使用的圖像的高度。(伸展或縮小圖像)
*/

    context.drawImage(source, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);


/*
HTMLCanvasElement.toDataURL() 方法傳回一個包含圖檔展示的 data URI 。可以使用 type 參數其類型,預設為 PNG 格式。圖檔的分辨率為96dpi。

如果畫布的高度或寬度是0,那麼會傳回字元串“data:,”。
如果傳入的類型非“image/png”,但是傳回的值以“data:image/png”開頭,那麼該傳入的類型是不支援的。
Chrome支援“image/webp”類型。
文法
canvas.toDataURL(type, encoderOptions);
參數

type 可選
圖檔格式,預設為 image/png
encoderOptions 可選
在指定圖檔格式為 image/jpeg 或 image/webp的情況下,可以從 0 到 1 的區間内選擇圖檔的品質。如果超出取值範圍,将會使用預設值 0.92。其他參數會被忽略。
傳回值

包含 data URI 的DOMString。
*/
     dataUrl = canvas.toDataURL('image/jpeg', quality); //此處第二個參數為0.2,這樣裁剪後隻有兩三百K
    console.log(dataUrl);
    $('#images_show ul').append('<li><img width="88px" height="88px" src="' + dataUrl + '"/></li>');

    if(files.length>)
    {
        // 遞歸,否則無法全部壓縮
        gallery_get_base64(files);


    }
    else{

        plus.nativeUI.closeWaiting();

    }

}
    source.src=cur_file;





                }

           

在做完壓縮操作後,我的DOM裡面就有了IMG的清單,裡面的src是canvas.todataURL傳回的資料,該資料是通過base64編碼的,此時擷取到這個資料,我也改變了我的上傳檔案方式,并非通過uploader對象的addFile來上傳了。(因為該接口的參數必須是檔案的路徑)而是直接通過addDATA完成任務,因為現在已經是一串編碼資料,再此還要注意的是,datatoURL傳回的字元串頭部是有識别的字元串的,開頭那一部分我們在背景接收的時候并不需要,那也不是base64編碼的結果,隻是識别作用,是以我們可以把前面23個字元去掉 var base64=data.substr(23);然後把這個base64傳到背景

如果是多張圖檔則循環把img src裡面的字元串全部截取下來,通過upload對象的addDATA添加上去,注意該接口的參數key是不允許重複的否則會失敗,注意區分

在背景接收到base64資料後,解密并寫入檔案,(**注意**傳入背景的base64資料中‘+’号會被替換成空格,是以要将空格全部替換回來)下面是PHP例子:

<?php
    header("Content-Type: text/html; charset=utf-8");
$file_number=$_POST['file_number'];
$key=$_POST['key'];//标記,因為上傳的時候必須保證key是不一緻的,要擷取這個資料就通過某一特定标記+i(循環)擷取



for($i=;$i<$file_number;$i++)
{
    $cur_file=$key.$i;//假設key是 file 則,循環擷取的是  file0  file1 file2 ....

        $img = str_replace(' ', '+', $_POST[$cur_file]);//注意因為傳入到背景的資料+号會被替換成空格,故而此處要轉換回來,base64編碼結果有+号

    $img_decode64 = base64_decode($img);//base64解碼,
    $f = fopen('upload/'.$key.$i.'.jpg', 'w+');//寫入圖檔
    fwrite($f, $img_decode64);
    fclose($f);


}


?>

           

繼續閱讀