本文主要介绍如何通过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对象,这样就可以删除文件。最后,我们看看效果(样例代码点击下载)