THink PHP 3.2.3 + SQL Server2008 ,图片与二进制流互转的问题

时间:2022-03-08 03:50:46
         由于最近工作需要,所以使用了 THink PHP3.2.3 + SQL Server 2008 的组合。可能think php 不是为 sqlserver 量身定制的,所以用它的方法,更新、写入 sql server 中的数据时,总是转换语句失败(插入或者更新)。
     下面是关于图片表的结构:
表名 字段名 类型    允许空    默认值      字段说明
Picture Id int                                         主键
PictureBinary varbinary √ 图片数据
MimeType         nvarchar        图片类型
SeoFilename nvarchar         √
IsNew bit
UpdatedOnUtc datetime          创建时间

         要将图片的二进制流数据写入数据库,需将二进制数据转换为 16 进制字符(bin2hex函数可以做到)。同样,我们从数据库取出的数据,也是16进制字符,所以要将它还原为2进制数据的形式(hex2bin 函数可以做到。),才能写入文件成功。

         下面就直接贴代码了:
        将数据库中的数据,转换为图片的方法
/*根据图片id (id可能为如下的数组)获取图片信息,包括图片的二进制流和图片类型等   待将 二进制  处理为图片
    array(3) {
    [0] => string(4) "1166"
[1] => string(4) "1167"
[2] => string(4) "1168"
参数: 图片的id  数组形式
返回:所有图片的 名称
}*/
function getPic(array $arr){
// 会存在不同的管理员登陆后台  重复生成图片问题
for($i=0; $i<count($arr); $i++){
$where['Id']=$arr[$i];
$pic=M('Picture')-> where($where) -> find();
// 文件的后缀名
$az = explode('image/',$pic['mimetype']);
// var_dump($az);
if($az[1] == 'jpeg'){
$az[1] = 'jpg';
}
// var_dump($where['Id']);
// 因为 轮播图处  目前需要更新  需要重新生成 id 相同的图片的 信息  故 先关闭检查 功能
$fid = "fk_".$arr[$i];

//checkPic($arr[$i]);
if(checkPic($arr[$i])){
// 图片生成过 将图片名称 返回
$picInfo[$arr[$i]]=$_SESSION[$fid];

}else{
// 没有生成过图片  调用生成图片的函数
// $pic['picturebinary'] 图片的16进制数据
$filename = getPicFile($pic['picturebinary'],$az[1]);
// 让图片 id 与 图片名称一一对应
$picInfo[$arr[$i]]=$filename;
// $_SESSION 存储的值 不能是数字
$_SESSION[$fid ] = $filename;   
//将新的图片id和名称也存储再 S 缓存中 保存一周 60*60*24*7=604800
// $picinfoa[$fid] = $filename;
// S('picinfo', $picinfoa);
}
}
return $picInfo;
}
// 检查图片是否被生成过
function checkPic($pid){
$fid = "fk_".$pid;
// $cache = S('picinfo');
// $arr[$i]====图片id 避免重复生成图片, 将已 生成的图片 id 及 图片名称 存入session 传过来 图片 id 时,与 session 中存在的 图片 id  进行比对
foreach ($_SESSION as $key => $value) {
if($fid == $key){
// 图片已经生成过
return true;
}
}

//图片没有生成过
return false;
}
/*
参数:$im_data 从数据库中读出来的 16 进制的 字符串
返回:图片名称

 */
function getPicFile($im_data,$hz)
{

// 生成的图片保存路径
$filepath = './Public/img/';
// var_dump($hz);
// 随机生成文件名
do{
$filename = date('YmdHis',time()).rand(1000000,9999999).'.'.$hz;
}while(file_exists($filepath.$filename));
// var_dump($filename);
// 打开图片文件 b 代表 二进制形式
$newFile = fopen($filepath.$filename,"wb");
// 使用 hex2bin 将 16进制的字符串转换为 二进制的字符串
$image_data  = hex2bin ($im_data);
//  将二进制字符串写到图片文件中
fwrite($newFile, $image_data);
fclose($newFile); //关闭文件
return $filename;
}


上传图片,将图片转为二进制数据流,并 再次转为 16 进制字符,写入sql server数据库( 此处只能写原生语句)
// 上传图片处理
   public function upload()
   {
        // 获取到图片id  ,将图片 id 和 href 存入到 轮播图表
        //var_dump($_FILES);
        //var_dump($_POST);
        //die;
        include './Application/Admin/Conf/fileupload.php';
        $pp = 'slide';
        // var_dump($pdo);
        if($_FILES[$pp]['error'] == 0){
            // 图片表 的必须数据
            $data['UpdatedOnUtc'] = date('Y-m-d H:i:s',time());
            // 调用上传方法
            $binary = self::upload_file($pp);

            switch ($binary) {
                case 0:
                    return $this -> error('没有文件被上传',U('Admin/Site/index'));
                    break;
                case 1:
                    return $this -> error('对不起,您上传的图片类型不符合规则',U('Admin/Site/index'));
                    break;
                case 2:
                    return $this -> error('对不起,您上传的图片大小不符合规则',U('Admin/Site/index'));
                    break;
                case 3:
                    return $this -> error('对不起,文件读取失败',U('Admin/Site/index'));
                default:
                    $data['picdata'] = $binary;
                    break;
            }
            //var_dump($data);
            // die;
            // 有文件上传
            if(!$_POST['pid']){
                // pid 不存在,则说明是  全新上传   insert 操作   需要向  图片表  轮播图表 插入新的数据
                $sql = "insert into Picture(PictureBinary,MimeType,UpdatedOnUtc,IsNew,IsTransient) values({$data['picdata']['PictureBinary']}, '{$data['picdata']['MimeType']}', '{$data['UpdatedOnUtc']}', 1, 1)";
                $row = $pdo -> exec($sql);

                if($row){
                    $pid = $pdo -> lastInsertId();
                    // 将图片的 id 插入到 轮播图表 solid     picid href
                    $slid = M('slide');
                    $data['PicId'] = $pid;
                    $data['Href'] = I('post.slide-href');
                    $slid -> create($data);
                    $res = $slid -> add();
                    if(!$res){
                        return $this -> error('图片上传错误',U('Admin/Site/index'));
                    }
                    return $this -> success('轮播图上传成功',U('Admin/Site/index'));
                }
            }else{ 
                // 如果  pid 存在  则说明是更新  图片  只需要更新图片的 二进制流数据,  以及 轮播图表里面的 href
                $sql2 = "insert into Picture(PictureBinary,MimeType,UpdatedOnUtc,IsNew,IsTransient) values({$data['picdata']['PictureBinary']}, '{$data['picdata']['MimeType']}', '{$data['UpdatedOnUtc']}', 1, 1)";
                // echo $sql2;
                $row = $pdo -> exec($sql2);
                if(!$row){
                    // 更新失败
                    return $this -> error('对不起,轮播图更新失败',U('Admin/Site/index'));
                }
                $pid = $pdo -> lastInsertId();
                $sql1 = "update slide set href='{$_POST['slide-href']}',picid='{$pid}' where id={$_POST['sid']}";
                $r2 = $pdo -> exec($sql1);
                // var_dump($r2);
                // die;
                if(!$r2){
                    return $this -> error('对不起,轮播图链接更新失败',U('Admin/Site/index'));
                }
                return $this -> success('轮播图更新成功',U('Admin/Site/index'));
            }
        }else{
            // 没有文件上传 也有可能是只更新链接  轮播图表
            if($_POST['slide-href']){
                $sql = "update slide set href='{$_POST['slide-href']}' where id={$_POST['sid']}";
                $row = $pdo -> exec($sql);
                if($row){
                    //如果受影响行数存在 ,则说明更新成功
                    return $this -> success('更新链接成功',U('Admin/Site/index'));
                    
                }else{
                    return $this -> error('更新失败',U('Admin/Site/index'));
                }
            }
            return $this -> error('没有文件被上传',U('Admin/Site/index'));
        }
   }
   // 上传图片的判断
   private function upload_file($pp)
   {
        if($_FILES[$pp]['error'] == 0){
            // 有文件上传
            // 允许文件上传的类型
            $allowmime = ['image/bmp','image/gif','image/jpeg','image/png'];
            if(!in_array($_FILES['slide']['type'], $allowmime)){
                // return $this -> error('对不起,您上传的图片不符合规则',U('Admin/Site/index'));
                return 1;
            }
            // 允许上传的文件大小 1MB  1048576
            if($_FILES['slide']['size'] > 1048576){
                // return $this -> error('对不起,您上传的图片不符合规则',U('Admin/Site/index'));
                return 2;
            }
            //图片类型 大小 都符合要求后  将临时文件夹中的上传图片 转为 二进制数据流
            $data['MimeType'] = $_FILES[$pp]['type'];
            // 以二进制流的方式打开一个文件
            $fp  = fopen($_FILES[$pp]['tmp_name'], 'r');
            if(!$fp){
                // return $this -> error('对不起,读取文件失败!',U('Admin/Site/index'));
                return 3;
            }
            $data['PictureBinary'] = '0x'.bin2hex(stream_get_contents($fp));

            //关闭文件
            fclose($fp);
            return $data;
        }else{
            // 没有文件被上传 
            return 0;
        }
   }


这个问题最重要的是,插入二进制数据时,不能使用 单引号或者双引号将二进制数据括起来。否则,数据库会将它当成长字符串来处理,从而报错(不能将 narchar(max)隐式转换为varbinary(max))
        这是自己项目的小记录,也希望能帮助到有需要的人。O(∩_∩)O~

2 个解决方案

#1


大牛们,有更好的方法,也请不吝赐教,让我们学习 THink PHP 3.2.3 + SQL Server2008 ,图片与二进制流互转的问题

#1


大牛们,有更好的方法,也请不吝赐教,让我们学习 THink PHP 3.2.3 + SQL Server2008 ,图片与二进制流互转的问题