2012年4月21日星期六

【转】Ajax+PHP+jQuery图片截图上传

一、功能分析

用户直接上传图片,点击"上传"按钮之后,在图片预览图内可预览图片,然后进行图片的裁剪前预览,当点击"裁剪"按钮时确定裁剪图片,并在"裁剪结果"区域显示裁剪后的效果

(说明:我是将上传文件保存在"/uploads"文件夹中,而截图结果放在"/avatar"文件夹里)

实现效果预览:

 

 

二、解决方案

1、插件的选择

  • jQuery:这个是必备的一个插件可以到官网上下载

http://docs.jquery.com/Downloading_jQuery

  • imgAreSselect:这个是实现客户端上图片区域选择的

http://odyniec.net/projects/imgareaselect/

  • uploadify:实现文件的上传的功能,支持多文件上传,且可定制性非常强。

http://www.uploadify.com/download/

上面的插件是用在客户端上,其实在我这个程序里写PHP时也用了一些插件。其实我之所以写"图像剪裁上传"的起源是因为我看了《PHP快速开发工具箱》想自己练习一下的。该书是有一个网址(http://www.pluginphp.com/),里面有整本书的代码,而且每个插件都相应的demo,非常不错。下面是用到的PHP插件:

  • PIPHP_UploadFile.php:这是一个文件上传功能的php文件

http://www.pluginphp.com/plug-in11.php

  • PIPHP_ImageCrop.php:这个php文件是具有对图片进行裁剪的功能

http://www.pluginphp.com/plug-in15.php

 

2、客户端与服务器之间的交互图

为了便于理解,我先把交互图放在这里。其中绿色部分是客户端的主要步骤、粉红色是服务器端的主要步骤,服务器与客户端之间的交互通过AJAX完成。可以发现,大部分的操作在客户端进行,服务器端与客户端之间的交流只是简单的JSON数据,因此这样给用户的体验是非常高的。

截图 1 客户端与服务器之间交互图

 

3、客户端文件

展示给用户的是html页面,为了学习并巩固CSS知识,就和DIV+CSS搭建了下面这样一个前台页面,见截图 2

截图 2 前台页面

跟客户端有关的文件主要是一个index.html,而在这个文件里面会引用其他的插件文件,因此可以说,客户端方面只有一个html文件。

另外,由于这里主要讨论客户端与浏览器之间的交互,因此略过CSS方面的内容。这里只列出HTML的代码,首先是<head>部分:

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<link rel="stylesheet" type="text/css" href="/imgareaselect/css/imgareaselect-default.css" />

<link rel="stylesheet" type="text/css" href="layout.css" />

<link href="/uploadify/uploadify.css" type="text/css" rel="stylesheet" />

 

<script type="text/javascript" src="/js/jquery-1.6.2.min.js"></script>

<script type="text/javascript" src="/imgareaselect/scripts/jquery.imgareaselect.pack.js"></script>

<script type="text/javascript" src="/uploadify/swfobject.js"></script>

<script type="text/javascript" src="/uploadify/jquery.uploadify.v2.1.4.min.js"></script>

<title>图片剪切上传</title>

</head>

可以看得出来主要是引用一些插件的文件。上面的文件(包括CSS文件与js文件)都可以从我给的链接里下载到,只是样式表layout.css是我自己写的样式表,大家可以根据自己的CSS知识写出。

接下来是body部分,也许这么看代码比较乱,推荐使用一些带有高亮显示的工具来查看这些代码,比如DreamWeaver等IDE,实在不行,也可以用火狐的"查看源代码"来看。(火狐不仅是一个好浏览器,更是一个极棒的调试器!)

为了方便起见,我去掉诸如"导航条"、页脚版权声明等点缀部分,只给出必要的html代码。

<body class="home">

<form>

    <fieldset>

    <legend>图片上传</legend>

     <div id="queue"></div>

<input type="file" name="file" id="pic_upload"/><br />

<a id='uploadLink' href="javascript:$('#pic_upload').uploadifyUpload();">上传图片 </a>

<div id="uploadInfo"></div>

</fieldset>

</form>

 

    <div id="twoColLayout">

    <div id="primaryContent">

<div >

    <h3>图片预览</h3>

    <div id='oriImage' class="img_v">

      

</div>                    

</div>

 

<div >

裁剪结果:

<div id="cropResult" class="cbb" >

      

</div>

</div>

</div>

 

<div id="sideContent">

裁剪预览:

<div id="preview">

 

</div>

</div>

</div>

</body>

上面我用颜色区分开主要DIV区,这三块分别代表"上传图片区"、"大图展示区"、"截图结果区"与"选择预览区"。其中三个粗体部分是带有ID属性的空DIV,用来放图片用的。(到时时候动态加载图片到这些DIV中),因此这段代码形成的HTML框架如截图 2所示。(蓝色线条是block元素边界,此效果较是由火狐的插件制作而成):

截图 3 页面大体框架

基本的准备工作已经完成,待会儿再继续在这个框架上添加代码。咱们先介绍一下服务器上的PHP是怎么个情况。

 

4、服务器文件

服务器上主要用 到两个PHP文件,一个用来处理上传图片的process.php,另外一个则是处理图片截图用的crop.php。不过,process.php文件包 括插件PIPHP_UploadFile.php,而crop.php中包括PIPHP_ImageCrop.php插件。(这些插件的地址我在上面已经 给出了)

=======

process.php主要接收上传图片,设置限制(比如文件的大小与格式),处理一些上传错误等,最后返回给客户端JSON,里面包含了所上传文件的一些信息(比如路径、大小等);当在客户端点击"上传"按钮的时候,会用异步(AJAX)的方式调用这个php文件。

=======

crop.php主要负责真正裁剪上传的图片,当在客户端返回裁剪的位置后(点击"裁剪"按钮后),以异步方式将数据以JSON的方式传递给服务器,crop.php真正裁剪图片后,将图片另存到网络的目录下,同时返回此图片的存储路径,然后再让客户端显示图片即可

 

三、用到的技术摘要

现在根据上面的交互图继续完善代码。因此我这节会交叉地完善html、js与php代码,并不会单独分开完善,这样在逻辑上会更好理解。

声明:新增的代码部分用粗体表示

 

1、uploadify上传

在html的head部分加入<script>标签,里面开始写主要的处理程序:

<script type="text/javascript" src="/uploadify/swfobject.js"></script>

<script type="text/javascript"  src="/uploadify/jquery.uploadify.v2.1.4.min.js"></script>

    <script type="text/javascript">

  $(document).ready(function(){

        //uploadify设置

            $('#pic_upload').uploadify({

                'uploader' : '/uploadify/uploadify.swf',

                'script' : 'process.php',        

                'cancelImg' : '/uploadify/cancel.png',

            });

  }

    </script>

</head>

上面的代码只是uploadify这一个插件的配置项而已。为了增强用户体验可以详细配置其他选项,这参考这个插件的官方文档:http://www.uploadify.com/documentation/。 上面的'script'选项就是选择服务器的处理脚本,我们这里就使用process.php了。上传文件到服务器后会让服务器自动调用这个程序。那么客 户端怎么知道服务器的process.php调用完了呢?如何获取process.php反馈回来的信息呢?——其实uploadify它提供了一个"触 发"选项onComplete,就是用来处理服务器的反馈信息的,我们稍后再写这个选项的内容,先看看process.php是返回哪些内容的呢。

 

2、process.php反馈上传信息

process.php的任务就是给浏览器返回JSON数据(至于什么是 JSON请参考其它教程,把JSON想像成"键/值"对就可以了,它很方便数据的传输与读取)。在PHP里,一般是先把数据整理成数组的形式,然后使用 json_encode()把数据转换成JSON。那process.php应该给浏览器返回什么样的数据呢?

  • 文件是否成功上传        ->    message
  • 文件的上传状态代码        ->    code
  • 文件上传的存放路径        ->    path
  • 图片的宽度                ->    width
  • 图片的高度                 ->    height
  • 图片的缩放比例            ->    scale
  • 图片的名称                ->    name

其中之所以设置图片的缩放比例scale, 是因为如果用户上传的图片尺寸太大(比如800x800),浏览器中的DIV会被"撑开",布局会被打乱。因此我们限定在浏览器显示图片的时候任何一边长 不能超过400px,否则在显示的时候以等比例缩放。(比如上面的800x800的图上会显示成400x400的,然后浏览器同时设置scale为 0.5)。

    另外,这个php文件是调用了PIPHP_UploadFile.php这个插件,用来将上传的文件进行"鉴别"与"搬移"。

下面是process.php的程序:

<?php

require_once(dirname(__FILE__)."/../PIPHP_UploadFile.php");

    $response=array(

        'message'=>'未知上传错误',

        'path'=>'',

        'code'=>-4, //上传结果代码,0表示成功,-1表示失败

        'width'=>100,

        'height'=>100,

        'scale'=>1,        //比例尺

        'name'=>''

    );

if (!empty($_FILES))

{

    $name='picture';

    $uploadFile='uploads/';

    $maxLen=9*1024*1024;

 

    $result=PIPHP_UploadFile($name,$uploadFile,$maxLen);

    

    

    $response['code']=$result[0];

    //简单汇报成功情况

    if($result[0]==0)

    {

        $response['message']='上传成功!';

        //$response['message']=$result[2];

        $response['path']=$result[1];

        $response['name']=$result[2];

        

        //获取图像的高度与宽度

        $fileName=iconv("utf-8","gb2312",$result[2]);

        list($width,$height)=getimagesize($_SERVER['DOCUMENT_ROOT'].$uploadFile.$fileName);

        $response['width']=$width;

        $response['height']=$height;

    }

    else

    {            

        switch($result[0])

        {

            case -1: $response['message']="上传失败"; break;

            case -2: $response['message']="文件类型错误";break;

            case -3: $response['message']="文件大小超过限制";break;

            default: $response['message']="错误代码:$result[0]";    

        }    

    }

}

else{

     $response['message']="上传文件出现错误!"."<br/>";

}

    $json_str=json_encode($response);

    echo $json_str;

?>

其实这个程序因为有了if判断语句而显示有点大,其实逻辑还是挺简单的。无论如何,这个程序返回的我上面说的有关图片的上传信息(放在$json_str这变量里了)

 

3、继续改进uplodify的配置

从上面知道,process.php返回的是一个$json_str变量,它里面有图像的路径,这样我们就可以在浏览器中显示图片啦!(注意此时显示的图片已经是在服务器上了)

现在添加uploadify的'onComplete'选项,它告诉浏览器当process.php返回数据时应该怎么做。

$('#pic_upload').uploadify({

    'uploader' : '/uploadify/uploadify.swf',

    'script' : 'process.php',        

    'cancelImg' : '/uploadify/cancel.png',

 

  'onComplete': function(event, ID, fileObj, response, data) {

        json_str=JSON.parse(response);

        var maxSize=400;

        var width=json_str.width;

        var height=json_str.height;

        var scale=json_str.scale;

        if(json_str.code == 0)

        {

             $('#uploadInfo').html(json_str.message+'<br/>平均上传速度:'+ data.speed.toFixed(2) + 'Kb/s');

             //对图像进行缩放

             if(json_str.width > maxSize || json_str.height >maxSize){

                if( json_str.width > json_str.height)

                {

                    width = maxSize;

                    height = maxSize / json_str.width * json_str.height;

                    json_str.scale = maxSize / json_str.width;

                }

                else

                {

                    height = maxSize;

                    width = maxSize / json_str.height * json_str.width;

                    json_str.scale = maxSize / json_str.height;

                }

             }

            

             $('#oriImage').html('<img src="/'+json_str.path+'" width=+'+width+' height='+height+' />');            

             //同时插入预览图

             $('#preview').empty().html('<img src="/'+json_str.path+'" width=+'+width+' height='+height+' />').css({

                    overflow:'hidden',

                    width:'150px',

                    height:'150px'

             });                         

        }

        else{

            $('#uploadInfo').html('错误代码['+json_str.code+']:<p>'+json_str.message+'</p>');

        }

  },

});

…    

这里的程序主要 做两件事,首先(第一种颜色标志处)显示上传的图,不过如果图片太大的话就应该显示缩放后的图,同时将缩放的比例保存到scale变量中;然后(第二处颜 色标志处)再初始化裁剪预览图(用jQuery方法),注意这里只是初始化并没有动态显示裁剪预览图,动态显示部分要用imgAreaSelection 这个插件完成。下面就开始讲这个插件吧

 

4、用imgAreaSelection获取截图点坐标

关于imgAreaSelection的使用说明请到官方上查看,这里就不再细讲。

由于图片是动态加载的,所以不能事先将这个插件应用到图像上。我们可以设置一个按钮,当图片上传显示后,我们点击这个"加载裁剪框"按钮,将这个插件绑定到图像上。所以我们先在html上添加一个按钮,我是加载那个"图像预览"的DIV里:

<div >

    <h3>图片预览</h3>

<button id="initCrop">加载裁剪框</button>

                    <div id='oriImage' class="img_v">

      

</div>                    

</div>

然后在head中的<script>标签中写点击事件处理程序:

$(document).ready(function(){

        $('#pic_upload').uploadify({

                …

        });

        //加载裁剪框    

        $('#initCrop').click(function(e){

                ias=$('#oriImage img').imgAreaSelect({instance:true});

                ias.setOptions({ aspectRatio: '1:1', handle:true,

                                 hide:false,

                                 onSelectChange:preview2,

                                 onSelectEnd: function (img, selection) {

                                    json_str.x1=selection.x1;

                                    json_str.y1=selection.y1;

                                    json_str.cropWidth=selection.x2-selection.x1;

                                    json_str.cropHeight=selection.y2-selection.y1;

                                 },

                                });                    

        });

}

</script>

</head>

这里的onSelectChange选项就是当改变裁剪框时所要调用的函数,这里使用preview2函数名作为值,这个函数我是另外写在下面的,当然你也可以使用匿名函数的。我是为了强调这个函数,所以写成实名函数:

//图像预览函数

    function preview2(img, selection) {

        realWidth=json_str.width * json_str.scale;

        realHeight=json_str.height * json_str.scale;

        sizeWidth=150;sizeHeight=150;

        var scaleX = sizeWidth / selection.width ;

        var scaleY = sizeHeight / selection.height ;

    

        $('#preview img').css({

            width: Math.round(scaleX * realWidth) + 'px',

            height: Math.round(scaleY * realHeight) + 'px',

            marginLeft: - Math.round(scaleX * selection.x1) + 'px',

            marginTop: -Math.round(scaleY * selection.y1) + 'px'

        });

    };

这个函数的功能是实现动态显示截图区域图像的,这个区域大小是150x150像素的一个小框,这里它动态加载css,注意要跟上一节中的uploadify中onComplete中预加载此截图框要联系起来。那里它是这么设置的:

//同时插入预览图

             $('#preview').empty().html('<img src="/'+json_str.path+'" width=+'+width+' height='+height+' />').css({

                    overflow:'hidden',

                    width:'150px',

                    height:'150px'

             });                         

        }

注意overflow:hidden的含义是将图像里超过这150x150像素的图像隐藏起来。其实这种方法借鉴自http://odyniec.net/projects/imgareaselect/examples-callback.html

另外的这插件中的onSelectEnd选项配置:当鼠标离开拖选框时,将此裁剪区域的左上角坐标与裁剪区的长、宽存储到json_str变量中,到时候传送给crop.php函数

 

5、将json_str数据传送给crop.php

使用jQuery中的ajax()方法将json_str变量传送给服务器。先在html中添加一个"裁剪"按钮:

<form id="cropData" >

<input type="submit" name="submit" id="crop" value="裁剪" />

</form>

这里我使用了<form>元素,其实没有必要,可以是任何元素,因为我们使用的是强大的jQuery的ajax()方法。

当用户确定要裁剪时,按下此按钮就会调用ajax()方法。我们将处理程序写在head部分的<script>元素中:

//加载裁剪框    

    $('#initCrop').click(function(e){

                …

    });

//裁剪动作,将数据传给服务器,同时ajax返回图片

    $("#crop").click(function(e){

        if(!(typeof json_str == 'undefined'))

        {                    jsondata='data='+JSON.stringify(json_str);

                    $.ajax({

                            type:"POST",

                            url:"crop.php",

                            data:jsondata,//$('#cropData').serialize(),

                            success:function(msg){

                                $("#cropResult").html('<img src="/'+msg+'"/>');//成功之后就清除发表内容

                                //$("#cropResult").html(msg);                                                        

                            }            

                        });

                }

                else

                {

                    alert('please load image first');

                }

                //关闭默认的提交动作    

                return false;

            });

//图像预览函数

    function preview2(img, selection) {…};

上面的代码就等待着crop.php把文件路径传送回来,一旦传送回来,'success'选项所配置的函数就会将图片显示在id为'cropResult'的DIV里面了。

 

6、crop.php对上传图片真正裁剪

这个crop.php文件功能也很简单,通过浏览器返回给的json_str 变量,由于该变量包含截图需要的起点坐标与裁剪的高度、宽度信息,然后调用PIPHP_ImageCrop.php插件,对图像进行真正的裁剪。然后将裁 剪后的图像保存在另外文件夹中(我是将上传文件保存在uploads文件夹中,而截图结果放在avatar文件夹里),并将目标文件夹的路径返回给浏览 器,让浏览器显示裁剪后的图片。

这个程序的源代码:

<?php

require_once('../PIPHP_ImageCrop.php');

    $json_str=json_decode($_POST['data']);

    $x=$json_str->x1;

    $y=$json_str->y1;

    $scale=$json_str->scale;

    $cropWidth=$json_str->cropWidth;

    $cropHeight=$json_str->cropHeight;

    $path=$json_str->path;

    $filename=$json_str->name;

    $tofilename=iconv("utf-8","gb2312",$filename);

    

    $realX=$x/$scale;

    $realY=$y/$scale;

    $realWidth=$cropWidth/$scale;

    $realHeight=$cropHeight/$scale;

    

    $cropedImage=PIPHP_ImageCrop('http://'.$_SERVER['SERVER_NAME'].'/'.$path, $realX, $realY, $realWidth, $realHeight);

    

    $targetDir='avatar/';

    $targetFile=$targetDir.$tofilename;

    

    imagejpeg($cropedImage,$_SERVER['DOCUMENT_ROOT'].$targetFile);

    

    echo $targetDir.$filename.'?'.time();

    ?>

需要注意的,最后返回的数据后加上'?'.time();是为了防止浏览器的缓存,如果不加此句,当用户完成截图后,重要再裁剪图片时,并不会马上更新裁剪得到的图片

好了,至此基本功能已经实现了,至于细节方面可以自己修改。


摘自:http://www.cnblogs.com/boychenney/archive/2011/10/08/2203036.html

2012年4月19日星期四

jQuery获取Select选择的Text和 Value(转)

原文地址:http://blog.chinaunix.net/u3/111819/showart_2197944.html

jQuery获取Select选择的Text和Value:
语法解释:
1. $("#select_id").change(function(){//code...}); //为Select添加事件,当选择其中一项时触发
2. var checkText=$("#select_id").find("option:selected").text();
//获取Select选择的Text
3. var checkValue=$("#select_id").val(); //获取Select选择的Value
4. var checkIndex=$("#select_id ").get(0).selectedIndex; //获取Select选择的索引值
5. var maxIndex=$("#select_id option:last").attr("index"); //获取Select最大的索引值
jQuery设置Select选择的 Text和Value:
语法解释:
1. $("#select_id ").get(0).selectedIndex=1; //设置Select索引值为1的项选中
2. $("#select_id ").val(4); // 设置Select的Value值为4的项选中
3. $("#select_id option[text='jQuery']").attr("selected", true);
//设置Select的Text值为jQuery的项选中
jQuery添加/删除Select的Option项:
语法解释:
1. $("#select_id").append("<option value='Value'>Text</option>");
//为Select追加一个Option(下拉项)
2. $("#select_id").prepend("<option value='0'>请选择</option>");
//为Select插入一个Option(第一个位置)
3. $("#select_id option:last").remove(); //删除Select中索引值最大Option(最后一个)
4. $("#select_id option[index='0']").remove(); //删除Select中索引值为0的Option(第一个)
5. $("#select_id option[value='3']").remove(); //删除Select中Value='3'的Option
5. $("#select_id option[text='4']").remove(); //删除Select中Text='4'的Option

http://www.cnblogs.com/SAL2928/archive/2008/10/28/1321285.html

jquery radio取值,checkbox取值,select取值,radio选中,checkbox选中,select选中,及其相关
获 取一组radio被选中项的值
var item = $('input[@name=items][@checked]').val();
获 取select被选中项的文本
var item = $("select[@name=items] option[@selected]").text();
select下拉框的第二个元素为当前选中值
$('#select_id')[0].selectedIndex = 1;
radio单选组的第二个元素为当前选中值
$('input[@name=items]').get(1).checked = true;

获取值:

文本框,文本区域:$("#txt").attr("value");
多选框 checkbox:$("#checkbox_id").attr("value");
单选组radio: $("input[@type=radio][@checked]").val();
下拉框select: $('#sel').val();

控制表单元素:
文本框,文本区域:$("#txt").attr("value",'');//清空内容
$("#txt").attr("value",'11');//填充内容

多选框checkbox: $("#chk1").attr("checked",'');//不打勾
$("#chk2").attr("checked",true);//打勾
if($("#chk1").attr('checked')==undefined) //判断是否已经打勾

单选组 radio: $("input[@type=radio]").attr("checked",'2');//设置value=2的项目为当前选中项
下拉框 select: $("#sel").attr("value",'-sel3');//设置value=-sel3的项目为当前选中项
$("<option value='1'>1111</option><option
value='2'>2222</option>").appendTo("#sel")//添加下拉框的option
$("#sel").empty();//清空下拉框

----------------------------------------------------------------------------------------------------

//遍历option和添加、移除option
function changeShipMethod(shipping){
var len = $("select[@name=ISHIPTYPE] option").length
if(shipping.value != "CA"){
$("select[@name=ISHIPTYPE] option").each(function(){
if($(this).val() == 111){
$(this).remove();
}
});
}else{
$("<option value='111'>UPS
Ground</option>").appendTo($("select[@name=ISHIPTYPE]"));
}
}


//取得下拉��的�取值

$(#testSelect option:selected').text();
或$("#testSelect").find('option:selected').text();
或$("#testSelect").val();
//////////////////////////////////////////////////////////////////
记 性不好的可以收藏下:
1,下拉框:

var cc1 = $(".formc select[@name='country']
option[@selected]").text(); //得到下拉菜单的选中项的文本(注意中间有空格)
var cc2 = $('.formc select[@name="country"]').val(); //得到下拉菜单的选中项的值
var cc3 = $('.formc select[@name="country"]').attr("id"); //得到下拉菜单的选中项的ID属性值
$("#select").empty();//清空下拉框 //$("#select").html('');
$("<option value='1'>1111</option>").appendTo("#select")//添加下拉框的option

稍微解释一下:
1.select[@name='country'] option[@selected] 表示具有name 属性,
并 且该属性值为'country' 的select元素 里面的具有selected 属性的option 元素;
可以看出有@开头的就表示后面跟 的是属性。

2,单选框:
$("input[@type=radio][@checked]").val(); //得到单选框的 选中项的值(注意中间没有空格)
$("input[@type=radio][@value=2]").attr("checked",'checked');
//设置单选框value=2的为选中状态.(注意中间没有空格)

3,复选框:
$("input[@type=checkbox][@checked]").val(); //得到复选框的选中的第一项的值
$("input[@type=checkbox][@checked]").each(function() { //由于复选框一般选中的是多个,所以可以循环输出
alert($(this).val());
});

$("#chk1").attr("checked",'');//不打勾
$("#chk2").attr("checked",true);// 打勾
if($("#chk1").attr('checked')==undefined){} //判断是否已经打勾


当然jquery的选择器是强大的. 还有很多方法.

<script src="jquery-1.2.1.js" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
$(document).ready(function(){
$("#selectTest").change(function()
{
//alert("Hello");
//alert($("#selectTest").attr("name"));
//$("a").attr("href","xx.html");
//window.location.href="xx.html";
//alert($("#selectTest").val());
alert($("#selectTest option[@selected]").text());
$("#selectTest").attr("value", "2");

});
});
</script>


<a href="#">aaass</a>

<!--下拉框-->
<select id="selectTest" name="selectTest">
<option value="1">11</option>
<option value="2">22</option>
<option value="3">33</option>
<option value="4">44</option>
<option value="5">55</option>
<option value="6">66</option>
</select>
jquery radio取值,checkbox取值,select取值,radio选中,checkbox选中,select选中,及其相关获取一组radio被选中
项的值
var item = $('input[@name=items][@checked]').val();
获取select被选 中项的文本
var item = $("select[@name=items] option[@selected]").text();
select 下拉框的第二个元素为当前选中值
$('#select_id')[0].selectedIndex = 1;
radio单选组的第二个 元素为当前选中值
$('input[@name=items]').get(1).checked = true;
获取值:
文本 框,文本区域:$("#txt").attr("value");
多选框 checkbox:$("#checkbox_id").attr("value");
单选组radio: $("input[@type=radio][@checked]").val();
下拉框select: $('#sel').val();
控 制表单元素:
文本框,文本区域:$("#txt").attr("value",'');//清空内容
$("#txt").attr("value",'11');// 填充内容
多选框checkbox: $("#chk1").attr("checked",'');//不打勾
$("#chk2").attr("checked",true);// 打勾
if($("#chk1").attr('checked')==undefined) //判断是否已经打勾
单选组radio: $("input[@type=radio]").attr("checked",'2');//设置value=2的项目为当前选中项
下拉框 select: $("#sel").attr("value",'-sel3');//设置value=-sel3的项目为当前选中项
$("<optionvalue='1'& gt;1111</option><optionvalue='2'>2222</option&
gt;").appendTo("#sel")//添加下拉框的option
$("#sel").empty();// 清空下拉框

获取一组radio被选中项的值
var item = $('input[@name=items][@checked]').val();
获取select被选中项的文本
var item = $("select[@name=items] option[@selected]").text();
select下拉框的第二个元素为当 前选中值
$('#select_id')[0].selectedIndex = 1;
radio单选组的第二个元素为当前选中值
$('input[@name=items]').get(1).checked = true;
获取值:
文本框,文本区域:$("#txt").attr("value");
多选框 checkbox:$("#checkbox_id").attr("value");
单选组radio: $("input[@type=radio][@checked]").val();
下拉框select: $('#sel').val();
控 制表单元素:
文本框,文本区域:$("#txt").attr("value",'');//清空内容
$("#txt").attr("value",'11');// 填充内容
多选框checkbox: $("#chk1").attr("checked",'');//不打勾
$("#chk2").attr("checked",true);// 打勾
if($("#chk1").attr('checked')==undefined) //判断是否已经打勾
单选组radio: $("input[@type=radio]").attr("checked",'2');//设置value=2的项目为当前选中项
下拉框 select: $("#sel").attr("value",'-sel3');//设置value=-sel3的项目为当前选中项
$("<option value='1'>1111</option><option
value='2'>2222</option>").appendTo("#sel")//添加下拉框的option
$("#sel").empty();// 清空下拉框

2012年4月12日星期四

[转]js产生随机数

<script>
document.write(parseInt(
10*Math.random()));  //输出0~10之间的随机整数

document.write(Math.floor(Math.random()
*10+1));  //输出1~10之间的随机整数

function RndNum(n){
var rnd="";
for(var i=0;i<n;i++)
rnd
+=Math.floor(Math.random()*10);
return rnd;
}
document.write(RndNum(
4));  //输出指定位数的随机数的随机整数



引用部分:

1. 从1开始 至 任意值
parseInt(Math.random()
*上限+1);

2. 从任意值开始 至 任意值
parseInt(Math.random()
*(上限-下限+1)+下限);
function fRandomBy(under, over){
switch(arguments.length){
case 1: return parseInt(Math.random()*under+1);
case 2: return parseInt(Math.random()*(over-under+1) + under);
default: return 0;
}
}
document.write(fRandomBy(
1,100));  //输出指定范围内的随机数的随机整数
</script>

//给既定文本框按规则付不同的值[引申]
<script>
window.onload
=function(){
var o=document.getElementsByTagName('input');
o[
0].value=fRandomBy(1,10);
o[
1].value=fRandomBy(11,20);
o[
2].value=fRandomBy(1,100);
o[
3].value=fRandomBy(51,100);
}
</script>
1-10: <input type="text" /><br />
11-20: <input type="text" /><br />
1-100: <input type="text" /><br />
51-100: <input type="text" /><br />




<html>
<head>
<title>Math</title>
</head>
<body>
<script language="javascript" type="text/javascript">
total
= 0
for(i=1;i<=5000;i++)
{num
=Math.random();
total
+=num
}
average
= total/5000
average = Math.round(average*1000)/1000
document.write("<h1>平均数:"+average+"</h1>")
</script>
</body>
</html>

摘自:http://www.cnblogs.com/merrick/archive/2010/08/17/1801158.html

2012年4月9日星期一

【转】jquery实现在元素外单击,隐藏此元素

实现的思路是

假如在元素A外单击隐藏A

判断被鼠标按下时的元素 的所有父元素中是否包含了A,没有包含则可以知道鼠标是在A之外单击的,则隐藏。

 

代码如下:

  1. $(document).mouseup(function(event){  
  2.     if($(event.target).parents("A").length==0){  
  3.         $("A").hide();  
  4.     }  
  5. })  
摘自:http://blog.csdn.net/diyle/article/details/6325655

2012年4月1日星期日

[转]SQLite创建触发器 CREATE TRIGGER

CREATE TRIGGER

sql-statement ::= CREATE[TEMP|TEMPORARY]TRIGGERtrigger-name[BEFORE|AFTER]
database-eventON[database-name.]table-name
trigger-action
sql-statement ::= CREATE[TEMP|TEMPORARY]TRIGGERtrigger-nameINSTEAD OF
database-eventON[database-name.]view-name
trigger-action
database-event ::= DELETE|
INSERT
|
UPDATE
|
UPDATE OF
column-list
trigger-action ::= [FOR EACH ROW|FOR EACH STATEMENT][WHENexpression]
BEGIN
    
trigger-step;[trigger-step;]*
END
trigger-step ::= update-statement|insert-statement|
delete-statement|select-statement

CREATE TRIGGER语句用于向数据库schema中添加触发器。触发器是一些在特定的数据库事件(database-event) 发生时自动进行的数据库操作(trigger-action).

触发器可由在特殊表上执行的DELETE, INSERT, UPDATE等语句触发,或UPDATE表中特定的字段时触发。

现在SQLite仅支持FOR EACH ROW触发器,不支持FOR EACH STATEMENT触发。因此可以不用明确说明FOR EACH ROW .FOR EACH ROW的意思是由trigger-steps说明的SQL语句可能在(由WHEN子句决定的)数据库插入,更改或删除的每一行触发trigger.

WHEN子句和trigger-steps可以使用"NEW.column-name"和"OLD.column-name"的引用形式访问正在被插入,更改或 删除的行的元素,column-name是触发器关联的表中的字段名。OLD 和 NEW 引用只在触发器与之相关的trigger-event处可用,例如:

INSERT NEW可用
UPDATE NEW和OLD均可用
DELETE OLD可用

当使用WHEN子句,trigger-steps只在WHEN子句为真的行执行。不使用WHEN时则在所有行执行。

trigger-time决定了trigger-steps执行的时间,它是相对于关联行的插入,删除和修改而言的。

作为的一部分trigger-step的UPDATE 或 INSERT可以使用ON CONFLICT子句。 但若触发trigger的语句使用了ON CONFLICT子句,则覆盖前述的ON CONFLICT子句所定义的冲突处理方法。

关联表被撤销时触发器被自动删除。

不仅在表上,在视图上一样可以创建触发器,在CREATE TRIGGER语句中使用INSTEAD OF即可。 若视图上定义了一个或多个ON INSERT, ON DELETE, ON UPDATE触发器,则相应地对视图执行INSERT,DELETE 或UPDATE语句 不会出错,而会触发关联的触发器。视图关联的表不会被修改。(除了由触发器进行的修改操作)。

Example:

假设"customers"表存储了客户信息,"orders"表存储了订单信息,下面的触发器确保当用户改变地址时所有的 关联订单地址均进行相应改变:

CREATE TRIGGER update_customer_address UPDATE OF address ON customers BEGIN UPDATE orders SET address = new.address WHERE customer_name = old.name; END;

定义了该触发器后执行如下语句:

UPDATE customers SET address = ’1 Main St.’ WHERE name = ’Jack Jones’;

会使下面的语句自动执行:

UPDATE orders SET address = ’1 Main St.’ WHERE customer_name = ’Jack Jones’;

注意,目前在有INTEGER PRIMARY KEY域的表上触发器可能工作不正常。若BEFORE触发器修改了一行的 INTEGER PRIMARY KEY域,而该域将由触发该触发器的语句进行修改,则可能根本不会修改该域。 可以用PRIMARY KEY字段代替INTEGER PRIMARY KEY字段来解决上述问题。

一个特殊的SQL函数RAISE()可用于触发器程序,使用如下语法:

raise-function ::= RAISE ( ABORT,error-message)|
RAISE ( FAIL,
error-message)|
RAISE ( ROLLBACK,
error-message)|
RAISE ( IGNORE )

当触发器程序执行中调用了上述前三个之一的形式时,则执行指定的ON CONFLICT进程(ABORT, FAIL或者ROLLBACK) 且终止当前查询,返回一个SQLITE_CONSTRAINT错误并说明错误信息。

当调用RAISE(IGNORE),当前触发器程序的余下部分,触发该触发器的语句和任何之后的触发器程序被忽略并且 不恢复对数据库的已有改变。 若触发触发器的语句是一个触发器程序本身的一部分,则原触发器程序从下一步起继续执行。

使用DROP TRIGGER删除触发器

摘自:http://hi.baidu.com/%B6%BE%C1%FD/blog/item/d05c8643d6fedd0f72f05dfe.html

[转]sqlite 触发器

::----------SQL触发器-------------------------------------------------
1.删除
    
CREATE TRIGGER [Delete_TopAlexa] ON [dbo].[tb_WebSite] 
    
FOR DELETE 
    
AS
    
BEGIN TRANSACTION
        
delete from tb_alexa where websiteid=(select websiteid from deleted)
        
delete from tb_top100 where websiteid=(select websiteid from deleted)
        
IF (@@ERROR <> 0-- 如果有错误
        BEGIN
            
ROLLBACK TRANSACTION -- 回滚
        END
    
COMMIT TRANSACTION -- 提交事务
2.更新
    
create trigger Test_OK
    
on ok
    
for update
    
as
    
if update(name)
    
begin
        
update sname set sname.s_name=ok.name from ok,sname
        
where sname.n_id=ok.n_id
    
end

::
----------SQLite触发器-------------------------------------------------
1.删除
    
Create  Trigger MAIN.[UserOK_DeleteOK] AFTER DELETE On [UserOK] FOR EACH ROW
    
begin
        
delete from userbak where uid=old.uid ;
    
end;

2.更新
    
Create  Trigger MAIN.[UserOK_Update] AFTER UPDATE On [UserOK] FOR EACH ROW
    
begin
        
update userbak set username=new.username
        
where userbak.uid=old.uid ;
    
end;

摘自:http://blog.csdn.net/jiannye/article/details/6362673

【转】SQLite3 数据库使用

摘自:http://jianlee.ylinux.org/Computer/Server/sqlite.html

参考

  1. SQLite3 使用教学
  2. SQLite 简单教程

简介

特点

  • 软件属于公共财(public domain),SQLite可说是某种「美德软件」 (virtueware),作者本人放弃着作权,而给使用SQLite的人以下的「祝福」 (blessing):
    • May you do good and not evil. 愿你行善莫行恶
    • May you find forgiveness for yourself and forgive others. 愿你原谅 自己宽恕他人
    • May you share freely, never taking more than you give. 愿你宽心与 人分享,所取不多于你所施予
  • 支援大多数的SQL指令(下面会简单介绍)。
  • 一个文件就是一个数据库。不需要安装数据库服务器软件。
  • 完整的Unicode支援(因此没有跨语系的问题)。
  • 速度很快。

SQLite顾名思议是以SQL为基础的数据库软件,SQL是一套强大的数据库语言,主 要概念是由「数据库」、「资料表」(table)、「查询指令」(queries)等单 元组成的「关联性数据库」(进一步的概念可参考网络上各种关于SQL及关联性数 据库的文件)。因为SQL的查询功能强大,语法一致而入门容易,因此成为现今主 流数据库的标准语言(微软、Oracle等大厂的数据库软件都提供SQL语法的查询及 操作)。

Linux 下命令行操作

下面的命令都是在 Linux 下运行过,基于 Debian 5.0,其他 Linux 通常也没有 问题。其中 SQL 命令通常是不依赖于任何平台的。

创建数据库

创建数据库就是创建一个 SQLite 文件。

# sqlite3 test.db  # 当前目录下如果有 test.db ,那么就读取这个文件,否则创建。
SQLite version 3.5.9
Enter ".help" for instructions
sqlite> create table mytable(name varchar (40), age smallint);
sqlite> insert into mytable values ('Jian Lee',23);
sqlite> select * from mytable;
Jian Lee|23

上面简单使用几个 SQL 命令创建一个表,并插入一条记录,最后查看创建的表内 容。使用 .help 可以列出帮助信息。 .quit 可以退出。

SQL 指令简介

所有的 SQL 指令都以 ";" 号结尾。 "- -" 表示注释。

创建 table,插入记录

sqlite> create table 数据库 (名字, 嵌入式);
sqlite> insert into 数据库 values ('PostgreSQL','否');
sqlite> insert into 数据库 values ('MySQL','否');
sqlite> insert into 数据库 values ('SQLite','是');
ssqlite> select * from 数据库;
PostgreSQL|否
MySQL|否
SQLite|是

insert into 命令中不存在的字段可以用 NULL 代替。

瞧,优秀的程序设计的多么好!任何"字符串"都只是一个"标签",所以都能完美支持!

建立索引

创建索引可以加快访问速度。

sqlite> create index 数据库名字索引 on 数据库 (名字);

查询

更新或修改/删除

更新
sqlite> select * from 数据库;
PostgreSQL|否
MySQL|否
SQLite|是
sqlite> update 数据库 set 名字='SQLite3' where 名字='SQLite';
sqlite> select * from 数据库;
PostgreSQL|否
MySQL|否
SQLite3|是
删除
sqlite> select * from 数据库;
PostgreSQL|否
MySQL|否
SQLite3|是
sqlite> delete from 数据库 where 嵌入式='是';
sqlite> select * from 数据库;
PostgreSQL|否
MySQL|否

SQLite 的特别用法

修改 sqlite3 的默认分隔符(|)

sqlite> .separator "-"

shell 下访问

# sqlite3 test.db "select * from 数据库;"
PostgreSQL|否
MySQL|否

如果要执行多条 sql 语句,可以先生成这样的sql语句文件,比如 "sql.txt":

.output Somefile    // 这句话告诉sqlite把sql执行的结果输出到文件
create tab ... // 正常的sql语句
insert into ....
.quit // 告诉sqlite退出
然后, "cat sql.txt sqlite data.db"。sqlite就会执行sql.txt中的sql语

句,并把结果保存到Somefile中。

输出 HTML 表格

root@jianlee:~/lab/xml/crepo/test# sqlite3 -html test.db "select * from 数据库;"
<TR><TD>PostgreSQL</TD>
<TD>否</TD>
</TR>
<TR><TD>MySQL</TD>
<TD>否</TD>
</TR>

将数据库倒出来(输出 SQL 指令)

root@jianlee:~/lab/xml/crepo/test# sqlite3 test.db ".dump"  > test.sql
root@jianlee:~/lab/xml/crepo/test# cat test.sql
BEGIN TRANSACTION;
CREATE TABLE mytable(name varchar (40), age smallint);
INSERT INTO "mytable" VALUES('Jian Lee',23);
CREATE TABLE 操作系统 (title, length, year, starring);
INSERT INTO "操作系统" VALUES('Jian Lee',23,33,45);
CREATE TABLE 数据库 (名字, 嵌入式);
INSERT INTO "数据库" VALUES('PostgreSQL','否');
INSERT INTO "数据库" VALUES('MySQL','否');
CREATE INDEX 数据库名字索引 on 数据库 (名字);
COMMIT;

重建数据库(用上面倒出的 SQL 语句)

root@jianlee:~/lab/xml/crepo/test# sqlite3 重建数据库.db < test.sql
root@jianlee:~/lab/xml/crepo/test# sqlite3 重建数据库.db "select * from 数据库;"
PostgreSQL|否
MySQL|否

在大量插入资料时,你可能会需要先打这个指令:

begin;

插入完资料后要记得打这个指令,资料才会写进数据库中:

commit;

begin; 和 commit; 是 SQL 处理一个事务的语法。

数据表结构

SQLite 数据库的数据结构保存在 "sqlite_master" 表中,下例针对 rpm 的 yum 源里的 repodata 目录中的 other.sqlite.bz2 数据库进行查询:

# sqlite3 other.sqlite
SQLite version 3.5.9
Enter ".help" for instructions
sqlite> select * from sqlite_master;
table|db_info|db_info|2|CREATE TABLE db_info (dbversion INTEGER, checksum TEXT)
table|packages|packages|3|CREATE TABLE packages ( pkgKey INTEGER PRIMARY KEY, pkgId TEXT)
table|changelog|changelog|4|CREATE TABLE changelog ( pkgKey INTEGER, author TEXT, date INTEGER, changelog TEXT)
index|keychange|changelog|5|CREATE INDEX keychange ON changelog (pkgKey)
index|pkgId|packages|6|CREATE INDEX pkgId ON packages (pkgId)
trigger|remove_changelogs|packages|0|CREATE TRIGGER remove_changelogs AFTER DELETE ON packages BEGIN DELETE FROM changelog WHERE pkgKey = old.pkgKey; END sqlite>

但是你不能够对sqlite_master 表执行 DROP TABLE, UPDATE, INSERT or DELETE ,当你创建或者删除表的时候,sqlite_master 表会自动更新。你不能手 工改变 sqlite_master 表。

临时表的结构不会存贮到 sqlite_master 表中,临时表是存贮在另一个特殊的 表,叫做 "sqlite_temp_master"。"sqlite_temp_master" 表本身就是临时的。

将结果写到文件中

取自参考资料2的示例,我现在还用不到:

默认情况下,sqlite3会将结果发送到标准输出,你可以使用 ".output" 来改 变,只是将输出到的文件名作为参数传递给 .output,所有后面的查询结果都会 写到文件里。开头使用 ".output stdout" 会再次写到标准输出,例如:

    sqlite> .mode list
sqlite> .separator |
sqlite> .output test_file_1.txt
sqlite> select * from tbl1;
sqlite> .exit
$ cat test_file_1.txt
hello|10
goodbye|20
$

查询数据库结构

列出所有数据表 (.tables)
sqlite> .tables
changelog db_info packages
sqlite>

".tables" 命令和一下的查询相似:

    SELECT name FROM sqlite_master
WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'
UNION ALL
SELECT name FROM sqlite_temp_master
WHERE type IN ('table','view')
ORDER BY 1
列出特殊表的索引 (.indices)

".indices" 命令以相似的方法列出一个特殊表的索引。".indices" 命令以一个 数据表的名字作为参数。

.schema

不带参数的 .schema 命令会显示用来创建数据库的 "CREATE TABLE and CREATE INDEX" 语句,如果你给一个表名为参数,他会显示用来创建表和索引(如果有的 话)的 CREATE 语句。

sqlite> .schema
CREATE TABLE changelog ( pkgKey INTEGER, author TEXT, date INTEGER, changelog TEXT);
CREATE TABLE db_info (dbversion INTEGER, checksum TEXT);
CREATE TABLE packages ( pkgKey INTEGER PRIMARY KEY, pkgId TEXT);
CREATE INDEX keychange ON changelog (pkgKey);
CREATE INDEX pkgId ON packages (pkgId);
CREATE TRIGGER remove_changelogs AFTER DELETE ON packages BEGIN DELETE FROM changelog WHERE pkgKey = old.pkgKey; END;
schema>
sqlite> .schema changelog
CREATE TABLE changelog ( pkgKey INTEGER, author TEXT, date INTEGER, changelog TEXT);
CREATE INDEX keychange ON changelog (pkgKey);

常用命令

.databases 列出数据库文件名
.tables ?PATTERN? 列出?PATTERN?匹配的表名
.import FILE TABLE 将文件中的数据导入的文件中
.dump ?TABLE? 生成形成数据库表的SQL脚本
.output FILENAME 将输出导入到指定的文件中
.output stdout 将输出打印到屏幕
.mode MODE ?TABLE? 设置数据输出模式(csv,html,tcl…
.nullvalue STRING 用指定的串代替输出的NULL串
.read FILENAME 执行指定文件中的SQL语句
.schema ?TABLE? 打印创建数据库表的SQL语句
.separator STRING 用指定的字符串代替字段分隔符,这个很有用!
.show 打印所有SQLite环境变量的设置
.quit 退出命令行接口

SQLite 结构分析

每一个 SQLite 数据库都有一个叫 SQLITE_MASTER 的表, 它定义数据库的模式。 SQLITE_MASTER 表看起来如下:

CREATE TABLE sqlite_master (
type TEXT,
name TEXT,
tbl_name TEXT,
rootpage INTEGER,
sql TEXT
);

对于表来说,type 字段永远是 'table',name 字段永远是表的名字。所以,要 获得数据库中所有表的列表, 使用下列SELECT语句:

SELECT name FROM sqlite_master
WHERE type='table'
ORDER BY name;

对于索引,type 等于 'index', name 则是索引的名字,tbl_name 是该索引所属 的表的名字。 不管是表还是索引,sql 字段是原先用 CREATE TABLE 或 CREATE INDEX 语句创建它们时的命令文本。对于自动创建的索引(用来实现 PRIMARY KEY 或 UNIQUE 约束),sql字段为NULL。

SQLITE_MASTER 表是只读的。不能对它使用 UPDATE、INSERT 或 DELETE。 它会 被 CREATE TABLE、CREATE INDEX、DROP TABLE 和 DROP INDEX 命令自动更新。

临时表不会出现在 SQLITE_MASTER 表中。临时表及其索引和触发器存放在另外一 个叫 SQLITE_TEMP_MASTER 的表中。SQLITE_TEMP_MASTER 跟 SQLITE_MASTER 差 不多,但它只是对于创建那些临时表的应用可见。如果要获得所有表的列表, 不 管是永久的还是临时的,可以使用类似下面的命令:

SELECT name FROM
(SELECT * FROM sqlite_master UNION ALL
SELECT * FROM sqlite_temp_master)
WHERE type='table'
ORDER BY name

触发器

假设"customers"表存储了客户信息,"orders"表存储了订单信息,下面的触发器 确保当用户改变地址时所有的 关联订单地址均进行相应改变:

CREATE TRIGGER update_customer_address UPDATE OF address ON customers
BEGIN
UPDATE orders SET address = new.address WHERE customer_name = old.name;
END;

定义了该触发器后执行如下语句:

UPDATE customers SET address = '1 Main St.' WHERE name = 'Jack Jones';

会使下面的语句自动执行:

UPDATE orders SET address = '1 Main St.' WHERE customer_name = 'Jack Jones';

注意,目前在有INTEGER PRIMARY KEY域的表上触发器可能工作不正常。若 BEFORE触发器修改了一行的 INTEGER PRIMARY KEY域,而该域将由触发该触发器 的语句进行修改,则可能根本不会修改该域。 可以用PRIMARY KEY字段代替 INTEGER PRIMARY KEY字段来解决上述问题。

再举 1 例,在 primary.sqlite.bz2 数据库中:

CREATE TRIGGER removals AFTER DELETE ON packages
BEGIN
DELETE FROM files WHERE pkgKey = old.pkgKey;
DELETE FROM requires WHERE pkgKey = old.pkgKey;
DELETE FROM provides WHERE pkgKey = old.pkgKey;
DELETE FROM conflicts WHERE pkgKey = old.pkgKey;
DELETE FROM obsoletes WHERE pkgKey = old.pkgKey;
END;

SQLite 的 C 语言编程

执行sql语句

int
sqlite3_exec ( sqlite3 *db, // 使用 sqlite3_open () 打开的数据库对象。
const char *sql, // 一条待查询的 SQL 语句
sqlite3_callback, // 自定义的回调函数,对查询结果每一行都执行一次这个函数
void *,
char **errmsg
);
<example>

这是最常用的执行 sql 语句的调用。简单的参数含意标在上面函数中,下面对重
要参数含意详细注释:

- 第 4 个参数 "void *" 是调用者所提供的指针,可以传递任何一个指针参数到
这里,这个参数最终会传到回调函数里面,这个指针比较重要,可以用来作参
数的传递。如果不需要传递指针给回调函数,可以填NULL。等下我们再看回调
函数的写法,以及这个参数的使用。

- 第 5 个参数 "char ** errmsg" 是错误信息。注意是指针的指针。sqlite3里
面有很多固定的错误信息。执行 sqlite3_exec 之后,执行失败时可以查阅这
个指针(直接 printf("%s\n",errmsg))得到一串字符串信息,这串信息告诉
你错在什么地方。sqlite3_exec函数通过修改你传入的指针的指针,把你提供
的指针指向错误提示信息,这样sqlite3_exec函数外面就可以通过这个
char*得到具体错误提示。

说明:通常, sqlite3_callback 和它后面的 void * 这两个位置都可以填
NULL。填NULL表示你不需要回调。比如你做 insert 操作,做 delete 操作,就
没有必要使用回调。而当你做 select 时,就要使用回调,因为 sqlite3 把数据
查出来,得通过回调告诉你查出了什么数据。

** exec 的回调函数
<example>
typedef int
(*sqlite3_callback) (void *, // 这就是上面函数传递的 void * 参数,需要强制类型转换后才能使用。
int, // 查询结果的列数,即有多少个字段数
char **, // 保存查询结果
char ** // 各个字段的名字
);

回调函数必须定义成上面这个函数的类型。下面给个简单的例子:

不使用回调查询数据库

上面介绍的 sqlite3_exec 是使用回调来执行 select 操作。还有一个方法可以 直接查询而不需要回调。虽然回调显得代码整齐,但有时候你还是想要非回调的 select 查询。这可以通过 sqlite3_get_table 函数做到。

int
sqlite3_get_table (sqlite3 *, // 打开的数据库对象指针
const char * sql, // 要查询的 sql 语句
char *** resultp, // 查询结果
int * nrow, // 查询出多少条记录(即查出多少行)
int * ncolumn, // 多少个字段(多少列)
char ** errmsg // 错误信息
);

第3个参数是查询结果,它依然一维数组(不要以为是二维数组,更不要以为是三 维数组)。它内存布局是:第一行是字段名称,后面是紧接着是每个字段的值。 下面用例子来说事。

下面给个简单例子:

int main( int , char ** )
{
sqlite3 * db;
int result;
char * errmsg = NULL;
char ** dbResult; //是 char ** 类型,两个*号
int nRow, nColumn;
int i , j;
int index;

result = sqlite3_open( "c:\\Dcg_database.db", &db );

if( result != SQLITE_OK )
{
//数据库打开失败
return -1;
}

//数据库操作代码
//假设前面已经创建了 MyTable_1 表
//开始查询,传入的 dbResult 已经是 char **,这里又加了一个 & 取地址符,传递进去的就成了 char ***
result = sqlite3_get_table( db, "select * from MyTable_1", &dbResult, &nRow, &nColumn, &errmsg );
if( SQLITE_OK == result )
{
//查询成功
index = nColumn; //前面说过 dbResult 前面第一行数据是字段名称,从 nColumn 索引开始才是真正的数据
printf( "查到%d条记录\n", nRow );

for( i = 0; i < nRow ; i++ )
{
printf( "第 %d 条记录\n", i+1 );
for( j = 0 ; j < nColumn; j++ )
{
printf( "字段名:%s ß> 字段值:%s\n", dbResult[j], dbResult [index] );
++index; // dbResult 的字段值是连续的,从第0索引到第 nColumn - 1索引都是字段名称,从第 nColumn 索引开始,后面都是字段值,它把一个二维的表(传统的行列表示法)用一个扁平的形式来表示
}
printf( "-------\n" );
}
}

//到这里,不论数据库查询是否成功,都释放 char** 查询结果,使用 sqlite 提供的功能来释放
sqlite3_free_table( dbResult );

//关闭数据库
sqlite3_close( db );
return 0;
}

操作二进制

sqlite 操作二进制数据需要用一个辅助的数据类型:sqlite3_stmt * 。这个数 据类型记录了一个"sql语句"。为什么我把 "sql语句" 用双引号引起来?因为你 可以把 sqlite3_stmt * 所表示的内容看成是 sql语句,但是实际上它不是我们 所熟知的sql语句。它是一个已经把sql语句解析了的、用sqlite自己标记记录的 内部数据结构。正因为这个结构已经被解析了,所以你可以往这个语句里插入二 进制数据。当然,把二进制数据插到 sqlite3_stmt 结构里可不能直接 memcpy ,也不能像 std::string 那样用 + 号。必须用 sqlite 提供的函数来插入。

写入二进制

要插入二进制,前提是这个表的字段的类型是 blob 类型。假设有这么一张表:

create table Tbl_2( ID integer, file_content  blob )

首先声明

sqlite3_stmt * stat;

然后,把一个 sql 语句解析到 stat 结构里去:

sqlite3_prepare( db, "insert into Tbl_2( ID, file_content) values( 10, ? )", -1, &stat, 0 );

上面的函数完成 sql 语句的解析。

  • 第一个参数跟前面一样,是个 sqlite3 * 类型变量
  • 第二个参数是一个 sql 语句。这个 sql 语句特别之处在于 values 里面有个 ? 号。在sqlite3_prepare函数里,?号表示一个未定的值,它的值等下才插入。
  • 第三个参数我写的是-1,这个参数含义是前面 sql 语句的长度。如果小于 0,sqlite会自动计算它的长度(把sql语句当成以\0结尾的字符串)。
  • 第四个参数是 sqlite3_stmt 的指针的指针。解析以后的sql语句就放在这个结构里。
  • 第五个参数我也不知道是干什么的。为0就可以了。

如果这个函数执行成功(返回值是 SQLITE_OK 且 stat 不为NULL ),那么下面 就可以开始插入二进制数据。

sqlite3_bind_blob( stat, 1, pdata, (int)(length_of_data_in_bytes), NULL );
// pdata为数据缓冲区,length_of_data_in_bytes为数据大小,以字节为单位

这个函数一共有5个参数。

  • 第 1 个参数:是前面prepare得到的 sqlite3_stmt * 类型变量。
  • 第 2 个参数:?号的索引。前面prepare的sql语句里有一个?号,假如有多个?号 怎么插入?方法就是改变 bind_blob 函数第2个参数。这个参数我写1,表示 这里插入的值要替换 stat 的第一个?号(这里的索引从1开始计数,而非从0 开始)。如果你有多个?号,就写多个 bind_blob 语句,并改变它们的第2个 参数就替换到不同的?号。如果有?号没有替换,sqlite为它取值null。
  • 第3个参数:二进制数据起始指针。
  • 第4个参数:二进制数据的长度,以字节为单位。
  • 第5个参数:是个析够回调函数,告诉sqlite当把数据处理完后调用此函数来 析够你的数据。这个参数我还没有使用过,因此理解也不深刻。但是一般都填 NULL,需要释放的内存自己用代码来释放。

bind完了之后,二进制数据就进入了你的"sql语句"里了。你现在可以把它保存到数据库里:

int result = sqlite3_step( stat );
通过这个语句,stat 表示的sql语句就被写到了数据库里。 最后,要把 sqlite3_stmt 结构给释放:
sqlite3_finalize( stat ); //把刚才分配的内容析构掉

读出二进制

先声明 sqlite3_stmt * 类型变量:

sqlite3_stmt * stat;

然后,把一个 sql 语句解析到 stat 结构里去:

sqlite3_prepare( db, "select * from Tbl_2", -1, &stat, 0 );

当 prepare 成功之后(返回值是 SQLITE_OK ),开始查询数据。

int result = sqlite3_step( stat );

这一句的返回值是 SQLITE_ROW 时表示成功(不是 SQLITE_OK )。

你可以循环执行 sqlite3_step 函数,一次 step 查询出一条记录。直到返回值 不为 SQLITE_ROW 时表示查询结束。

然后开始获取第一个字段:ID 的值。ID是个整数,用下面这个语句获取它的值:

int id = sqlite3_column_int( stat, 0 );
//第2个参数表示获取第几个字段内容,从0开始计算,因为我的表的ID字段是第一个字段,因此这里我填0

下面开始获取 file_content 的值,因为 file_content 是二进制,因此我需要 得到它的指针,还有它的长度:

const void * pFileContent = sqlite3_column_blob( stat, 1 );
int len = sqlite3_column_bytes( stat, 1 );

这样就得到了二进制的值。把 pFileContent 的内容保存出来之后,不要忘了释 放 sqlite3_stmt 结构:

sqlite3_finalize( stat ); //把刚才分配的内容析构掉

重复使用 sqlite3_stmt 结构

如果你需要重复使用 sqlite3_prepare 解析好的 sqlite3_stmt 结构,需要用函 数: sqlite3_reset。

result = sqlite3_reset(stat);

这样, stat 结构又成为 sqlite3_prepare 完成时的状态,你可以重新为它 bind 内容。