毕设论文写的差不多了,写的时候一直在想那个没完成的功能,就是在系统中加一个批量导入功能。于是,在完成论文内容后开始着手编写这部分代码。PS:恰逢Github又挂了,所以只能先在记录了。
Thinkphp文件上传
Thinkphp自带文件上传功能,主要有单文件上传和多文件上传两种方式,由于对本次设计区别不是很大,这里不做过多介绍,具体参见文档http://www.kancloud.cn/manual/thinkphp/1876
上传操作
ThinkPHP文件上传操作使用Think\Upload
类,根据官方文档,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
类中携带以下属性:
这里同样圈红部分是常用参数,这里需要注意下,
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中屏蔽编码机制。具体代码参考如下:
这里讲些注意点,表单中的
我是添加了
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;
}
}
}
});
这里processData
和contentType
是用来屏蔽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会自动进行覆盖更新数据,不用担心报错跳出的情况。
后记
当然,这里也会带来一个问题,批量导入过程中我不知道哪些数据重复主键,这也算毕设中的一个问题,等待以后有空再考虑吧……