yii2 mpdf下载pdf文件配合ZipArchive压缩的性能优化和采坑经验
yii2 mpdf下载pdf文件配合ZipArchive压缩的性能优化和采坑经验
- 一、基本准备
- 1. 环境配置:
- 2. 前提条件
- 3. 业务需求
- 4. 采坑点及业务难点
- 二、业务过程
- 1. 第一步:将每条数据根据视图文件渲染,生成一个pdf文件,保存在服务器上
- 2. 第二步:将生成好的pdf文件们,放入压缩包
- 3. 视图层,执行回调函数,下载压缩文件
一、基本准备
1. 环境配置:
语言:PHP:7.0.9
服务器:Apache:2.4
框架:Yii2
2. 前提条件
项目里安装好mpdf插件,
可以使用composer require "mpdf/mpdf": ">=7.0"进行安装
查看mPDF官方手册
查看mPDF的GitHub
3. 业务需求
- 后台列表页面,根据搜索结果进行下载pdf文件,最终打包成压缩文件,进行下载
- 每条数据为一个pdf文件
- pdf的文件名包含中文字符(A232131-张三.pdf)

4. 采坑点及业务难点
- 往压缩文件里添加文件时
$zip->addFile($savePath. $dir_name, $dir_name),参数1(文件名)不得含有中文. - 解决上述问题时,当时没想着文件名用拼接的方式,一直穿,完整的再使用
iconv()方法进行转编码,问题是可以解决,但是会非常影响性能,因为多次进行重命名.查看当时思路来源.后面会附上当时使用转编码及扫描文件的方式执行成功的代码,1000多条数据就得5分钟. - 创建
xxx.zip文件时,使用touch($zipPath)进行创建. 不能使用fopen($zipPath, "w")进行创建,否则后面在执行$zip->close()时候会无法关闭. - 压缩文件地址
$zipPath,一开始使用静态地址,本地环境(Windows)重新生成压缩包的话,会覆盖旧的压缩文件,但是在线上(linux)环境下,会无法覆盖文件,导致每次下载,都会将旧的压缩文件地址返回.
二、业务过程
1. 第一步:将每条数据根据视图文件渲染,生成一个pdf文件,保存在服务器上
/*** 导出pdf*/
public function actionExportPdf()
{set_time_limit(0);ini_set('memory_limit', '1024M');$this->response->format = Response::FORMAT_JSON;$searchModelClass = $this->searchModelClass;$searchModel = new $searchModelClass();$params = $this->request->queryParams;// 设置分页信息$params['per-page'] = 50;$params['page'] = 1;Yii::$app->request->setQueryParams($params);$dataProvider = $searchModel->search($this->request->queryParams);$dataProvider->getModels();$pages = $dataProvider->getPagination();$pageCount = $pages->pageCount;// pdf文件资源路径$time = date("YmdHis", time());$uploadPath = Yii::getAlias(Yii::$app->params['uploadPath']);$savePath = "$uploadPath/tmp/pdf/contract/";$zipPath = "$uploadPath/tmp/pdf/contract_$time.zip";// 如果文件或文件夹不存在,则生成if (!file_exists($savePath)) {mkdir($savePath, 0777, true);}$files = [];// 按分页进行创建pdf文件for ($i = 1; $i <= $pageCount; $i++) {$params['page'] = $i;Yii::$app->request->setQueryParams($params);$dataProvider = $searchModel->search($this->request->queryParams);// 如果search模型里的关联查询,没有with全,这里一定要继续添加with,否则在大量数据的情况下,非常影响性能$dataProvider->query->with(['netSchool', 'category']);$models = $dataProvider->getModels();foreach ($models as $model) {if (!isset($model->user)) {continue;}// 期望最终的文件名格式( A451252-张三.pdf ) 这里拼接出完整的文件名// 重要:后续需要用此名进行重新命名$username = trim($model->user->name ?: '用户' . $model->uid);$currectionfileName = sprintf('%s-%s.pdf', $model->number, $username);// 输出文件名命名格式为( A451252.pdf ) // 因为后续往压缩包添加文件的时候,含有中文名的文件,将不能被添加进去$fileName = $savePath . $model->number . '.pdf';// 重要:此方法,会省去我们后续需要扫码文件夹的操作,大大节省了性能(数据量成千上万)$files[$model->number] = $currectionfileName;// 重要:检测该文件是否存在,大大节约性能,每条记录生成pdf文件后,以后将不会再重新生成,省去渲染页面,写入pdf文件的操作if (file_exists($fileName)) {continue;}// 参数1:内容编码格式, // 参数2:文件显示格式(A4-L横向), // 参数3:路径中存一些临时文件,图片或字体,该文件需要有读写权限,建议放在框架的runtime目录下$mpdf = new mPDF(['mode' => 'utf-8','A4','tempDir'=>Yii::getAlias('@backend') . '/runtime/','',32,25,27,25,16,13]);// 插件一些配置,具体可以自行查看官方文档$mpdf->useAdobeCJK = true;$mpdf->autoScriptToLang = true;$mpdf->autoLangToFont = true;// 渲染视图$html = $this->renderPartial('view', ['model' => $model,]);// 将渲染的html写入$mpdf->WriteHTML($html);// 输出pdf文件, 因为我们需要下载很多的pdf文件, 所以会在之后进行压缩操作,再进行下载// 参数1:一定得具体到路径下的文件名 E:/backend/common/static/pdf/contract/A32132.pdf// 参数2: D 为直接下载, F 保存在服务器上,其他的自行查看官方文档,或者类文件$mpdf->Output($fileName, 'F'); }} // 下一步讲解zipFile()方法的具体实现return $this->zipFile($savePath, $zipPath, $files);
}
2. 第二步:将生成好的pdf文件们,放入压缩包
/*** 压缩文件* 参数1:pdf文件保存的地址,具体到文件夹* 参数2:压缩包所在的地址,具体到改文件名(..../xxx.zip)* 参数3:重要: 免去扫描文件的麻烦,节约服务器性能*/public function zipFile($savePath, $zipPath, $files) {// 创建压缩文件touch($zipPath);// 初始化$zip = new ZipArchive();$res = $zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE);if (!$res) {return $result['message'] = "文件开启失败.";}// 重要:前面因为保存了当前查询的信息,这里就可以明确知道所需要的文件信息,直接拼接使用就行// 而不需要使用扫码函数,对该目录进行扫码,再获取文件名,节约性能foreach($files as $dir_name => $nameBill) {$dir_name = $dir_name . '.pdf';// 文件添加到压缩包, 参数1:具体到某个文件, 参数2:建议直接使用文件名,防止自定义的名字会被覆盖问题$zip->addFile($savePath. $dir_name, $dir_name);// 将压缩包内的文件名改成我们最初期望的$zip->renameName($dir_name, $nameBill);};// 关闭插件服务, 至此,压缩文件已经生成,我们可以获取地址,直接访问就可以下载了$zip->close();// 设置下载信息if (!file_exists($zipPath)) {$result['name'] = "zip";$result['message'] = "压缩失败";$result['code'] = "404";}else{$result['name'] = "zip";$result['message'] = "压缩成功";$result['code'] = "200";// 拼接好下载地址$result['fileUrl'] = util::getUrl('static') . '/' . substr($zipPath, strpos($zipPath, 'upload'));}return $result;
}
3. 视图层,执行回调函数,下载压缩文件
// 导出pdf
$(document).on('click', '.btn-export-pdf', function (e) {event.preventDefault();var url = $(this).attr('href');var data = [];// 获取当前请求路径里的参数var req = url + location.search;$('#myModal').modal('show');$.get(req, function(response) {if (200 == response.code) {$('#myModal').modal('hide');downloadFile(response.fileUrl);}});
})function downloadFile(fileUrl)
{var $form = $('');$form.attr('action', fileUrl);$form.appendTo($('body'));$form.submit();
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
