[毕设项目]Thinkphp结合PHPExcel实现Excel数据的导入

毕设论文写的差不多了,写的时候一直在想那个没完成的功能,就是在系统中加一个批量导入功能。于是,在完成论文内容后开始着手编写这部分代码。PS:恰逢Github又挂了,所以只能先在记录了。

Thinkphp文件上传

Thinkphp自带文件上传功能,主要有单文件上传和多文件上传两种方式,由于对本次设计区别不是很大,这里不做过多介绍,具体参见文档http://www.kancloud.cn/manual/thinkphp/1876

上传操作

ThinkPHP文件上传操作使用Think\Upload类,根据官方文档,Upload类参数主要有以下几种:

[毕设项目]Thinkphp结合PHPExcel实现Excel数据的导入_第1张图片
Upload类支持的属性设置

其中,圈红部分是常用参数。
通过实例化 Upload类,我们就可以向后台传入文件了。我们结合官方文档进行解读:

$config = array(
    'maxSize'    =>    3145728,    //设置上传附件的大小
    'rootPath'   =>    './Uploads/',   //设置上传文件的根目录
    'savePath'   =>    '',    //设置上传文件的子目录
    'saveName'   =>    array('uniqid',''),  //设置文件保存规则
    'exts'       =>    array('jpg', 'gif', 'png', 'jpeg'), //设置上传文件的后缀
    'autoSub'    =>    true,     //设置保存上传文件采用子目录形式存储
    'subName'    =>    array('date','Ymd'),  //设置子目录创建方式
);
$upload = new \Think\Upload($config);  // 实例化上传类

Upload类也支持动态赋值,形式如$upload->maxSize = 3145728;,效果是一致的。

上传文件信息

通过上传。文件被放到指定文件夹中,并在Upload类中携带以下属性:

[毕设项目]Thinkphp结合PHPExcel实现Excel数据的导入_第2张图片
文件信息

这里同样圈红部分是常用参数,这里需要注意下, savename文件的保存名字是带后缀的。

多文件上传和单文件上传

两者区别较小(我只是根据API来说的,并未仔细阅读过源码),多文件上传无非是前端可提交多个文件数据,即标签可编写多个。而单文件上传无非是使用另一个API,即uploadOne方法,只是这种方法注意需要指定文件,如$info = $upload->uploadOne($_FILES['photo1']);

代码编写

对于后台文件上传这部分,我把代码编写到了\Application\Admin\Common\Common\function.php中,作为公共方法进行调用:

/**
 * 上传文件
 * @param array $fileName 文件
 * @param string $dir   文件上传子目录
 * @param string $saveName  文件名字保存规则
 * @return array 操作结果
 */
function uploadFile($fileName = array(), $dir = '', $saveName = '')
{
    $config = array(
        'maxSize' => 3145728,               //设置文件大小
        'exts' => array('xls', 'xlsx'),        //设置附件上传类型
        'rootPath' => './Public/Excel/',    //设置文件上传根目录
        'savePath' => $dir . '/',           //设置文件上传子目录
        'autoSub' => false,                 //自动使用子目录保存文件
        'saveName' => $saveName,            //上传文件的保存规则
    );
    $upload = new \Think\Upload($config);   //实例化上传类
    $info = $upload->uploadOne($fileName);  //上传单个文件
    if (!$info)
        return array('status' => 0);
    else
        return array('status' => 1, 'filepath' => $upload->rootPath . $info['savepath'] . $info['savename']);
}

jQuery实现Ajax异步提交文件上传

前端的东西算是新手,所以可能写的不是很清楚,并且有些地方是直接转载别人的话(我会标出来),见谅……

用过jquery的Ajax的人肯定都知道,Ajax的默认编码方式是application/x-www-form-urlencoded,此编码方式只能编码文本类型的数据,因此Ajax发送请求的时候,会把data序列化成 一个个String类型的键值对,此种传输数据的方式能够满足大部分应用场景,然而当传输的数据里有附件的时候,此序列化机制便是我们的绊脚石。Ajax本身的序列化机制的硬伤归其原因在于在html4的时代,没有FileReader接口,在页面里无法读取File(Blob)文件,用document.getElementById("文件控件的id").value只能拿到文件的name,因此去序列化去编码它也无从谈起。
——http://blog.csdn.net/qq_33556185/article/details/51086114

上面这段大概就能理解,Ajax本来是不支持文件上传,因为上传的是数组对象,而HTML5新增了一种数据对象FormData,可以快速拿到整个表单对象。而要作此操作,需要先在表单中添加enctype="multipart/form-data"属性,然后在Ajax中屏蔽编码机制。具体代码参考如下:


[毕设项目]Thinkphp结合PHPExcel实现Excel数据的导入_第3张图片
批量上传模态框效果图

这里讲些注意点,表单中的 我是添加了 style="display:none"属性的,所以界面中看到的并不是原生的 标签;这里的文本框是通过JS进行控制显示内容的,后面会讲到。
HTML代码部分完成后,只需要结合jQuery就可以完成前端向后端发送文件的请求 。以下是JS代码部分:

//批量添加
        $upload.click(function () {
            $upload_modal.modal('show');
            return false;
        });
        //input框中控制显示文件路径
        $('input[id=leftFile]').change(function () {
            $('#fileCover').val($(this).val());
        });
        //文件上传提交
        $upload_submit.click(function () {
            var fileExt = checkFile();
            if (fileExt[0] === '') {
                toastr.warning('请上传文件');
            } else if (fileExt[0] !== 'xls' && fileExt[0] !== 'xlsx') {
                toastr.warning('上传文件类型不支持');
            } else {
                var formData = new FormData($upload_form[0]);
                //TODO: Ajax异步文件提交
            }
        });

这里单独抽出来Ajax的异步提交内容:

$.ajax({
    url: 'uploadClass',
    type: 'post',
    data: formData,
    cache: false,
    processData: false,
    contentType: false,
    dataType: 'json',
    success: function (response) {
        switch (response.status) {
            case 'success': {
                toastr.success(response.message);
                $upload_modal.modal('hide');
                    $table.bootstrapTable('refresh');
                break;
            }
            case 'error': {
                toastr.error(response.message);
                $upload_modal.modal('hide');
                $table.bootstrapTable('refresh');
                break;
            }
        }
    }
});

这里processDatacontentType是用来屏蔽Ajax的编码机制,这样上传的FormData对象就不会上传到后台就变成其他编码的了。另外提一点,这里的dataType属性是指返回对象属性为JSON格式(这也是毕设中发现的一点)。
这样,前端就将文件数据传给后台了,后台Controller通过$_FILES[]数组进行获取,代码如下:

public function uploadClass()
    {
        $upload = uploadFile($_FILES['classFile'], 'Class', date('TmdHis'));  //获取上传的 文件
        if ($upload['status']) {
            $excel = import_excel($upload['filepath']);
            D('Clas')->upload($excel);
        }
        show('success', '批量添加成功');
    }

PHPExcel

PHPExcel是一款处理表格文件数据上传下载的插件,项目地址:https://github.com/PHPOffice/PHPExcel ,文档也在上面有,但是由于时间问题我没仔细阅读过,大部分还是根据别人的博客上的代码学习的。

引入PHPExcel插件

Thinkphp是将PHPExcel引入\Think\Library\Vendor 第三方扩展库中,Thinkphp自带引入函数 Vendor(),具体我就不介绍了,有兴趣可以查下官方文档。

代码编写

由于文档没有完整读过,所以这里贴代码了,使用起来很简单,这里我对表格内容转换为数组部分做了处理,使得获取的数据更符合我后台的数据库模型接口设计。这部分代码同样我存放在\Application\Admin\Common\Common\function.php中作为公共方法使用:

/**
 * 导入表格
 * @param $filePath 文件路径
 * @return array    表格数据
 */
function import_excel($filePath)
{
    // 判断文件是什么格式
    $type = pathinfo($filePath);
    $type = strtolower($type["extension"]);
    $type = $type === 'Excel5' ? $type : 'Excel2007';
    ini_set('max_execution_time', '0');
    Vendor('PHPExcel.Classes.PHPExcel');                    // 引入PHPExcel类
    $objReader = PHPExcel_IOFactory::createReader($type);   // 判断使用哪种格式
    $objPHPExcel = $objReader->load($filePath);             // 加载表格
    $sheet = $objPHPExcel->getSheet(0);              // 获取Excel表格sheet1页
    $highestRow = $sheet->getHighestRow();                  // 取得总行数
    $highestColumn = $sheet->getHighestColumn();            // 取得总列数
    //循环读取excel文件,读取一条,插入一条
    for ($j = 2; $j <= $highestRow; $j++) {                 // 从第一行开始读取数据
        for ($k = 'A'; $k <= $highestColumn; $k++) {        // 从A列读取数据
            $data[$j - 2][$objPHPExcel->getActiveSheet()->getCell("$k" . "1")->getValue()] = $objPHPExcel->getActiveSheet()->getCell("$k$j")->getValue();  // 读取单元格
        }
    }
    return $data;
}

补充:数据库模型设计

到这里前面的基本都讲清楚了,前端、后台的代码都贴完整了,这里主要是针对毕设项目做一个数据库模型的补充。

public function upload($jsonArray = array())
    {
        foreach ($jsonArray as $item) {
            $result = $this->Clas->add($item, $option = array(), $replace = true);
            if (!$result) return false;
        }
        return true;
    }

这里的add()方法的参数需要讲下,正常来说add()方法中只需要传格式规范的数据数组就可以了,但是如果遇到例如这里的批量上传可能会碰见数据主键重复的现象,如果对于大量数据来说,每次数据库录入重复数据报错一次会很影响速度,所以Thinkphp提供了一个参数(只支持3.2.3版本以上)$replace,当设置为true时,如果录入数据遇到重复主键,Thinkphp会自动进行覆盖更新数据,不用担心报错跳出的情况。

后记

当然,这里也会带来一个问题,批量导入过程中我不知道哪些数据重复主键,这也算毕设中的一个问题,等待以后有空再考虑吧……

你可能感兴趣的:([毕设项目]Thinkphp结合PHPExcel实现Excel数据的导入)