本文主要介紹如何通過PhoneGap調用攝像頭拍照,並且把拍照后的圖片自動發送到服務器。
處理文件上傳需要服務端的支持,為了簡單,我直接用了PHP,如下面的代碼所示:
<?php if ($_FILES["file"]["error"] > 0){ echo "Return Code: " . $_FILES["file"]["error"] . "<br />"; }else{ echo "Upload: " . $_FILES["file"]["name"] . "<br />"; echo "Type: " . $_FILES["file"]["type"] . "<br />"; echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />"; echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />"; if (file_exists("upload/" . $_FILES["file"]["name"])){ echo $_FILES["file"]["name"] . " already exists. "; }else{ move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]); echo "Stored in: " . "upload/" . $_FILES["file"]["name"]; } } ?>
代碼很簡單,其核心其實就是一句話:move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]) ,表示將name為"file"的文件保存到服務器的upload文件夾下。
我們可以自己寫一段HTML代碼測試服務端是否可用。如下所示:
<!doctype html> <html> <body> <form action="upload_file.php" method="post" enctype="multipart/form-data"> <label for="file">Filename:</label> <input type="file" name="file" id="file" /> <br /> <input type="submit" name="submit" value="Submit" /> </form> </body> </html>
通過瀏覽器訪問然后提交,就可以看到如下效果:
Upload: appmobi_iphone.js
Type: application/x-javascript
Size: 6.912109375 Kb
Temp file: C:\Windows\temp\php60ED.tmp
Stored in: upload/appmobi_iphone.js
服務端OK后,我們開始編寫PhoneGap代碼,首先看HTML代碼,非常簡單,只定義一個按鈕,然后引入一些腳本。如下所示:
<!DOCTYPE html> <html> <head> <title>PhoneGap Upload</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <meta name="format-detection" content="telephone=no" /> <style> .button-block { margin-bottom: 0; box-shadow: inset 0 1px 1px rgba(255, 255, 255, .4), 0 1px rgba(255, 255, 255, .8); display: block; padding: 11px 0 13px; margin-bottom: 10px; font-size: 16px; } .button-main{ color: #fff; text-shadow: 0 -1px 0 rgba(0, 0, 0, .3); background-color: #1eafe7; background-image: -webkit-linear-gradient(top, #1eafe7 0, #1a97c8 100%); background-image: linear-gradient(to bottom, #1eafe7 0, #1a97c8 100%); border: 1px solid #117aaa; font-weight: bold; line-height: 18px; text-align: center; vertical-align: top; cursor: pointer; border-radius: 3px; box-shadow: inset 0 1px 1px rgba(255, 255, 255, .4), 0 1px 2px rgba(0, 0, 0, .05); -webkit-transition: background .3s linear; } </style> </head> <body> <div class="button-block button-main" id='upload'>拍照上傳</div> <script src="js/cordova-2.6.0.js"></script> <script src="js/zepto.min.js"></script> <script src="js/when.js"></script> <script src="js/upload_pic.js"></script> </body> </html>
按鈕用CSS3來控制,同時引入PhoneGap的腳本和zepto.js(兼容jQuery接口,但是更為小巧,針對移動端優化)。由於PhoneGap調用本地設備都是異步的,需要開發人員提供回調。為了以同步的方式編寫異步代碼,我們引入了when.js,
upload_pic.js是我們的業務代碼。如下所示:
(function() { // 事件綁定 $("#upload").bind("click",function() { takePicture().then(uploadPicture).then(deletePictureFromCache); }); // 打卡攝像頭拍照 function takePicture() { var deferred = when.defer(), destinationType=navigator.camera.DestinationType, options = { quality: 100, destinationType: destinationType.FILE_URI //sourceType: Camera.PictureSourceType.PHOTOLIBRARY, //cameraDirection: Camera.Direction.FRONT, //targetWidth: 240, //targetHeight: 320, //correctOrientation: true }; navigator.camera.getPicture(function(data){ deferred.resolve(data); }, null, options); return deferred.promise } // 上傳圖片到服務器 function uploadPicture( imageURI ){ var deferred = when.defer(), options = new FileUploadOptions(); options.fileKey = "file", options.fileName = imageURI.substr(imageURI.lastIndexOf('/')+1); options.mimeType = "image/jpeg"; var ft = new FileTransfer(); // 上傳回調 ft.onprogress = showUploadingProgress; navigator.notification.progressStart("", "當前上傳進度"); ft.upload( imageURI, encodeURI('http://10.4.45.90/upload/upload_file.php'), function(){ deferred.resolve( imageURI ); navigator.notification.progressStop(); } , null, options); return deferred.promise } // 顯示上傳進度 function showUploadingProgress( progressEvt ){ if( progressEvt.lengthComputable ){ navigator.notification.progressValue( Math.round( ( progressEvt.loaded / progressEvt.total ) * 100) ); } } // 從緩存中刪除圖片 function deletePictureFromCache( imageURI ){ window.resolveLocalFileSystemURI(fileURI, function( fileEntry ){ fileEntry.remove(); }, null); } })();
分析我們的業務,我們的業務流程是:1、打開攝像頭拍照; 2、上傳文件到服務器; 3、如果照片不再使用,需要將照片從緩存中刪除。
3個流程,后一個必須依賴前一個,也就是所只有拍照成功了,才能上傳到服務器,圖片才能刪除。由於每一個都是異步的,所以直接編寫代碼會顯得很亂,這就是為什么要引入when.js的原因。由於異步編程涉及到不少知識,
各位可以猛擊下這篇文章:基於事件的 JavaScript 編程:異步與同步。
再說下如何實現拍照、上傳並顯示進度以及從緩存中刪除圖片。
拍照很簡單,直接調用navigator.camera.getPicture即可,可以設置返回的格式,如URI或者base64加密的數據,官方推薦用前者,因為后者在某些Android手機會出現一些內存問題。
上傳文件主要調用 FileTransfer對象,它有一個upload方法,可以向指定的服務器POST數據,並可以在onProgress事件中捕獲到上傳文件。這個和HTML5中的XMLHttpRequest Level2標准中onprogress事件一致。
顯示上傳進度,我們直接調用了 navigator.notification.progressStart、navigator.notification.progressValue、navigator.notification.progressStop等方法,這幾個方法在PhoneGap API中好像沒提到,我是自己翻看源碼才
發現的 :-)
最后是刪除緩存中文件,PhoneGap拍照時,會把文件保存在SD卡中的/Android/data/com.flyingzl/cache目錄下,其中com.flyingzl是我們程序的packageName。可以見Cordova中的源碼:
protected static String getTempDirectoryPath(Context ctx) { File cache = null; // SD Card Mounted if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { cache = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + ctx.getPackageName() + "/cache/"); } // Use internal storage else { cache = ctx.getCacheDir(); } // Create the cache directory if it doesn't exist if (!cache.exists()) { cache.mkdirs(); } return cache.getAbsolutePath(); }
PhoneGap也提供了獲取文件和刪除文件的接口,即 window.resolveLocalFileSystemURI。這個方法是異步的,其回調得到的就是FileEntry對象,這樣就可以刪除文件。最后,我們看看效果(樣例代碼點擊下載)