php异步处理下载文件,异步处理Excel文件导入【流程图+PHP示例】

面向管理后台的系统中,经常会有文件导入的需求。常规的做法就是同步等待,但在业务关系复杂(多表数据校验)、数据量较大的情况下,管理人员只能等结果,也可能会等到超时。

使用异步的话,将导入数据的功能与后端接口解耦,避免接口超时导致的任务中止,也无需前端只为了拿个结果一直保持连接等待。

前端在上传文件后,后端接口将导入任务推送(MQ、管道...)出去,然后直接返回前端。导入服务接到任务执行导入,并根据需求将实时导入状态维护到缓存中。前端查询/轮询后端从缓存取出当前导入状态。

流程图如下:

674e18dbc684

异步导入.png

简单的PHP + Swoole后端代码示例(实际就两个接口方法upFile、importStatus,和Task的导入处理):

/**

* Created by PhpStorm.

* User: wen

* Date: 2018/12/8

* Time: 11:09 PM

*/

require 'vendor/autoload.php';

use Swoole\Http\Server;

const BASE_DIR = __DIR__;

// 路由定义

$router = [

'GET' => [

'/importStatus' => 'importStatus' // 查询导入状态

],

'POST' => [

'/upFile' => 'upFile' // 上传导入文件

]

];

// ----SWOOLE-HTTP服务设置部分

$http = new Server("127.0.0.1", 9501);

$http->set([

'worker_num' => 2,

'task_worker_num' => 4,

]);

$http->on('request', function ($request, $response) use ($router, $http) {

$funName = $router[$request->server['request_method']][$request->server['request_uri']] ?? 'NotFound';

if (!function_exists($funName)){

return backJson($response, null, 404, 'ROUTER NOT FOUND');

}

try{

$funName($request, $response, $http);

}catch (Exception $e){

return backJson($response, null, 500, $e->getMessage());

}

});

// 实际导入操作

$http->on('Task', function (swoole_server $serv, $task_id, $from_id, $data) {

$redis = getNewRedis();

$status = [

'step' => 1, // 文件准备处理

'progressRate' => '',

'info' => [],

];

$redis->set($data, json_encode($status, JSON_UNESCAPED_UNICODE));

// 读取文件 使用了PhpOffice\PhpSpreadsheet解析EXCEL

$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load(BASE_DIR . '/' . $data);

$sheetData = $spreadsheet->getActiveSheet()->toArray(null, true, true, true);

$count = count($sheetData);

$status['step'] = 2; // 文件解析完成

$status['progressRate'] = "解析到文件数据{$count}条";

$redis->set($data, json_encode($status, JSON_UNESCAPED_UNICODE));

$names = [];

var_dump($sheetData);

foreach ($sheetData as $k => $item){

if (1==$k) continue; // 第一行为表头

if (empty($item['A'])) {

unset($sheetData[$k]);

$status['info'][] = "第{$k}行姓名为空";

continue;

}

$names[] = $item['A'];

}

$redis->set($data, json_encode($status, JSON_UNESCAPED_UNICODE));

// TODO: 验证数据库name是否已存在 插入等业务处理...(此处代码省略)

// TODO: 将进度维护到redis

});

$http->on('Finish', function () {});

// ----基础函数部分

function getNewRedis(){

($redis = new \Redis())->connect('127.0.0.1');

return $redis;

}

function backJson($response, $content, $statusCode=200, $msg=''){

$response->header('Content-Type', 'application/json');

$jsonData = [

'statusCode' => $statusCode,

'content' => $content,

'msg' => $msg,

];

$response->end(json_encode($jsonData, JSON_UNESCAPED_UNICODE));

return true;

}

function NotFound($request, $response){

return backJson($response, null, 404, 'ROUTER NOT FOUND');

}

// ----接口方法

// 上传文件

function upFile($request, $response, $server){

$file = $request->files['file'] ?? null;

if (empty($file)) { throw new Exception('未收到上传文件'); }

$importSN = md5($file['tmp_name'] . time()) . '.' . pathinfo($file['name'])['extension'];

$bol = move_uploaded_file($file['tmp_name'], BASE_DIR . '/' . $importSN);

if (false === $bol) { throw new Exception('文件处理异常'); }

$status = [

'step' => '0',

'progressRate' => '',

'info' => [],

];

getNewRedis()->set($importSN, json_encode($status, JSON_UNESCAPED_UNICODE));

$server->task($importSN);

return backJson($response, ['importSN'=>$importSN]);

}

// 查询导入状态

function importStatus($request, $response){

$importSN = $request->get['importSN'] ?? null;

if (!$importSN){ throw new Exception('导入任务编号不正确'); }

$redis = getNewRedis();

$content = $redis->get($importSN);

if (!$content){ throw new Exception('未查询到任务'); }

return backJson($response, json_decode($content));

}

$http->start();

PostMan访问示例:

674e18dbc684

屏幕快照 2018-12-09 上午4.06.17.png

674e18dbc684

屏幕快照 2018-12-09 上午4.04.16.png

674e18dbc684

屏幕快照 2018-12-09 上午4.04.50.png

这里主要是任务投递的渠道,如Channel、MQ服务、Unix Socket等。

Channel:最简单好用,同服务进程内通信,进程挂了就都gg

MQ服务:独立服务,简单通用,可以多服务器,可靠性高

Unix Socket:单服务器内进程间通信,偏底层

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/260243.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

tcp client.cs

public class stateobject { public socket worksocket null; public const int Buffer_Size2048; public byte[] buffer new byte[Buffer_size]; public stringbuilder sb new stringbuilder(); } 转载于:https://www.cnblogs.com/neumik/archive/2012/11/15/2771024.ht…

[python] 之 常用内建函数

本博客仅列举了一些常用的内建函数,欢迎大家补充! 1. dir([obj]) 显示对象的属性,若果没有提供参数,则显示全局变量的名字 2. help([obj]) 以一种整齐美观的方式,显示对象的文档字符串;如果没有提供任何参数…

python查询模块所有类_python 小技巧(import模块、查询类继承关系、安装包)

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢!在这里列举一些我使用Python时积累的小技巧。这些技巧是我在使用Python过程中经常使用的。之前很零碎的记在笔记本中,现在整理出来&am…

4.2 access函数实例

int access(const char *filenpath, int mode); 功 能: 确定文件或文件夹的访问权限。 mode,要判断的模式在头文件unistd.h中的预定义如下:#define R_OK 4 /* Test for read permission. */#define W_OK 2 /* Test for write permission. */#define X_OK…

php 简易 blog,PHP实现简易blog的制作

最近,有时间看了点PHP的代码。参考PHP100教程做了简单的blog,这里面简单的记录一下。首先是集成环境,这里选用的WAMP:http://www.wampserver.com/en/首先通过,phpMyAdmin创建一张blog表。纯界面操作,过程比…

jquery 事件对象属性小结

jquery 事件对象属性小结 使用事件自然少不了事件对象. 因为不同浏览器之间事件对象的获取, 以及事件对象的属性都有差异, 导致我们很难跨浏览器使用事件对象. jQuery中统一了事件对象, 当绑定事件处理函数时, 会将jQuery格式化后的事件对象作为唯一参数传入: $("#testDiv…

ABP文档 - Mvc 视图

文档目录 本节内容: 简介AbpWebViewPage 基类简介 ABP通过nuget包Abp.Web.Mvc集成到Mvc视图里,你可以像往常那样创建常规的视图。 AbpWebViewPage 基类 ABP也提供了AbpWebViewPage,它定义了一些有用的属性和方法,如果你使用启动模…

ThinkPad L440 FN键设置

刚入手了ThinkPad L440,用起来相当不错,嘿嘿! L440系统默认(F1-F12)键盘为系统默认功能键,主要控制音量、亮度、连接投影仪等。 因为编写程序需要调试,经常用到F10,F11等键&#xff…

离散数学反对称关系_《离散数学》学习记录 - 集合论

来源&#xff1a;北京大学《离散数学》公开课地址&#xff1a;https://www.bilibili.com/video/av18896337/?p122.1 有序对和卡氏积有序对<a,b>&#xff1a;有顺序&#xff0c;类似于数组&#xff0c;可以用集合定义。性质&#xff1a;有序对内元素对应相等卡氏积AB&…

收集的博客列表

前端&#xff1a; ———————————————————— 宅居 - 裸: http://otakustay.com/ 转载于:https://www.cnblogs.com/ccdc/archive/2012/11/21/2780879.html

php创建表并插入数据,php数据库操作-创建库和表以及插入数据

以上我们正确连接到了mysql数据库&#xff0c;本文将进一步创建数据库&#xff0c;表&#xff0c;在表中填充数据。大家知道连接上数据库才能进行操作&#xff0c;同样的代码搬过来/** 数据库操作*(创建数据库&#xff0c;表&#xff0c;插入数据&#xff0c;插入多条数据)** T…

C#配置及使用log4net

首先从官方网站http://logging.apache.org/log4net/下载最近版本的log4net组件。在程序中添加对log4net.dll的引用&#xff0c;就可以在程序中使用了。 下一步&#xff0c;编写配置文件&#xff0c;内容如下 <?xml version"1.0" encoding"utf-8" ?>…

ORACLE EBS常用表及查询语句(最终整理版)

建议去看参考二 参考一&#xff1a; call fnd_global.APPS_INITIALIZE(1318,50583,401) select fnd_profile.VALUE(ORG_ID) FROM DUAL select * from hr_operating_units hou where hou.organization_id204 --fn…

mysql触发器 当记录的指定字段发生变化时,更新表中的另外一个字段,或者更新另外一张关联表中关联记录的字段...

2019独角兽企业重金招聘Python工程师标准>>> 注意&#xff1a;语句中出现的old&#xff0c;new&#xff0c;now&#xff08;&#xff09;&#xff0c;都为数据库自带的关键字&#xff0c;此处不做解释。 两种情况&#xff1a; 第一种&#xff1a;一张表中&#xff0…

通用无线设备对码软件_珞光全新发布国产通用软件无线电平台 :USRP-LW N310!珞光品牌已实现国产替代...

USRP-LW N310是一种网络的软件定义无线电&#xff08;SDR&#xff09;&#xff0c;它提供了部署大规模的可靠的和容错性的分布式无线系统。USRP-LW N310通过引入远程执行任务的能力简化了对SDR系统的控制和管理&#xff0c;如更新软件&#xff0c;重新启动&#xff0c;工厂复位…

手把手玩转win8开发系列课程(2)

对win8开发&#xff0c;上一节我们对win8进行了简单的介绍&#xff0c;这一节我们来瞧一瞧他的开发环境搭建。 前奏。 这里所讲的win8开发&#xff0c;主要是指Windows8 app store 上开发&#xff0c;及metro ui或叫morden ui 程序的开发。传统桌面应用程序&#xff0c;网站应…

python通过什么来区分不同语句块_Python语言通过

【填空题】小块【填空题】离开;出发(n.)【填空题】好人啊中的 “ 啊 ” 读( )【填空题】“ 洁癖 ” 的正确读音是( )【单选题】The article suggests that when a person ________ under unusual stress he should be especially careful to have a well-balanced diet. (CET20…

【Android面试】Android面试题集锦 (陆续更新)

【Android面试】Android面试题集锦 (陆续更新) 分类&#xff1a; 【杂七杂八】2011-05-11 17:58 2064人阅读 评论(0) 收藏 举报一些常见的Android面试基础题做下总结&#xff0c;看看你能做出多少道? 1. Intent的几种有关Activity启动的方式有哪些&#xff0c;你了解每个含义吗…

cordova-plugin-app-version插件使用

此插件用来获取开发软件的版本号&#xff01;首先安装此插件&#xff1a; 命令行中输入 cordova plugin add cordova-plugin-app-version然后刷新项目&#xff0c;就会在在项目plugins文件夹下看到cordova-plugin-app-version,如下图所示接下来就是使用此插件的语句获取版本号c…

14.cookie与自动登陆

场景 webdriver可以读取并添加cookie。有时候我们需要验证浏览器中是否存在某个cookie&#xff0c;因为基于真实的cookie的测试是无法通过白盒和集成测试完成的。 另外更加常见的一个场景是自动登陆。有很多系统的登陆信息都是保存在cookie里的&#xff0c;因此只要往cookie中添…