博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

PhoneGap 拍照并上传到远程服务器

Posted on 2013-06-03 15:01  三块石头  阅读(21973)  评论(8编辑  收藏  举报

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