本文共 12511 字,大约阅读时间需要 41 分钟。
的文档是需要消费的,并且它的api请求是一个异步的过程,如果业务上用户不需要登录的时候,无法直接获取到翻译结果文档。因此,做了个异步返回的功能。
思路:
1、上传文档;
2、调用翻译接口;
3、通过kafka监听接口回调,处理返回数据,保存到本地(方便以后扩展,如加入用户信息,查看我的翻译记录)
4、前端监听后端服务,通过swoole的websocket实时请求文档数据
关键部分代码(赶工做的,未做优化):
//上传文件public function upload(){ header("Access-Control-Allow-Origin:*"); set_time_limit(300); $file = $_FILES['file']; if(!$file){ $show = array('status'=>422,'message'=>'请上传图片或者文档文件'); json($show); } $app = '2020bd'; if($_SERVER['SERVER_ADDR']=='127.0.0.1'){ $sPath = '/Users/kunyuan/www/activity/storage/'.$app.'/'; }else{ $sPath = '/var/www/html/activity/storage/'.$app.'/'; } if(!mk_dir($sPath)){ $show = array('status'=>500,'message'=>'目录不存在'); json($show); } $path_parts = pathinfo($file['name']); $ext = strtolower($path_parts["extension"]); $extAry = array( 'jpg','jpeg','png','docx','xls','xlsx','ppt','pptx','pdf' ); if(!in_array($ext, $extAry)){ $show = array('status'=>422,'message'=>'请上传图片或者文档文件'); json($show); } try{ //保存到服务器本地 $filename = date('Ymd'). '_' .rand(10000, 99999).'_'.time(); $tmpfile = $filename . '.'.$ext; $tmp = $file["tmp_name"]; $img = file_get_contents($tmp); $path1 = $sPath . $tmpfile; $res = file_put_contents($path1, $img); if(is_https()){ $url = 'https://'.$_SERVER['SERVER_NAME'].'/storage/'.$app.'/'.$tmpfile; }else{ $url = 'http://'.$_SERVER['SERVER_NAME'].'/storage/'.$app.'/'.$tmpfile; } $error = '上传失败'; if($res){ //判断文件类型,为了文件调用百度的图片接口或者文档接口 if(in_array($ext,array('jpg','jpeg','png'))){ $resExt = 'pic'; }else{ $resExt = 'docs'; } //保存数据库 $fileid = $this->common->setFiles(array( 'sourceFile' => $url, 'localFile' => $path1, 'type' => $resExt )); $show = array('status'=>0,'fileUrl'=>$url,'fileSize'=>ceil(filesize($path1)/1000)."k",'ext'=>$resExt, 'id' => $fileid,'resExt'=>$resExt); }else{ $show = array('status'=>500,'message'=>$error,'info'=>$info,'fileSize'=>ceil(filesize($path1)/1000)."k"); } json($show); }catch(Exception $e){ $show = array('status'=>$e->getCode(),'message'=>$e->getMessage()); json($show); }}
//调用百度接口public function api(){ //$type = I('type','docs'); $id = I('id',0); $callback = I('callback'); //判断是否为上传的文件 $data = $this->common->getFiles(array('id'=>$id)); if(empty($data)){ $show = array('status'=>420,'message'=>'数据有误'); json($show, $callback); } $type = $data['type']; switch($type){ case 'docs': $this->docsTran($data); break; case 'pic': $this->picTran($data); break; default: $this->docsTran($data); break; }}//文档翻译接口private function docsTran($data){ //die('ooo'); $file = $data['localFile'];//I('file'); $from = I('from', 'zh'); $to = I('to','en'); $callback = I('callback'); if(empty($file)){ $show = array('status'=>422,'message'=>'请上传文档文件'); json($show, $callback); } $path_parts = pathinfo($file); $ext = strtolower($path_parts["extension"]); $extAry = array( 'docx','xls','xlsx','ppt','pptx','pdf' ); if(!in_array($ext, $extAry)){ $show = array('status'=>423,'message'=>'请上传文档文件'); json($show, $callback); } if($from == $to){ $show = array('status'=>424,'message'=>'翻译语言不能与原文档言语一样'); json($show, $callback); } $arr = array( 'requestId' => uniqid(), 'sourceFile' => $data['sourceFile'], 'localFile' => '', 'type' => 'docs', 'content' => '', 'create_time' => time() ); $id = $this->common->set($arr); $params = array( 'appid' => $this->appid, //你的appid 'from' => $from, 'to' => $to, 'timestamp' => time(), //10位时间戳 'type' => $ext, //文档类型,根据具体文档而定 ); $seckey = $this->seckey; //你的密钥 ksort($params); $querySign = ''; foreach ($params as $key => $value) { $querySign .= $key . '=' . $value . '&'; } $filePath = $file; //文档路径 $params['sign'] = md5($querySign . '' . md5_file($filePath) . '' . $seckey); $url = 'http://fanyi-api.baidu.com/api/trans/vip/doctrans'; $header = array( 'Content-Type' => 'multipart/form-data', ); $params['file'] = '@' . realpath($imagePath); //高版本兼容 if (class_exists('\CURLFile')) { $params['file'] = new \CURLFile(realpath($filePath)); } $ch = curl_init(); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS,$params); curl_setopt($ch,CURLOPT_URL,$url); curl_setopt($ch,CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $result=curl_exec($ch); curl_close($ch); logs('Docs Result>>>'.$result); $callRet = json_decode($result, true); //print_R($result); if($callRet['error_code'] == 52000){ $arr = array( 'requestId' => $callRet['data']['requestId'], ); $this->common->update($arr, array('id'=>$id)); //$this->load->libraliry('Kafka'); $this->kafkaMsg($callRet['data']['requestId']); $show = array('status'=>0,'message'=>'翻译成功','requestId'=>$callRet['data']['requestId']); json($show, $callback); }else{ $show = array('status'=>$callRet['error_code'],'message'=>$callRet['error_msg']); json($show, $callback); }}//图片翻译接口private function picTran($data){ $file = $data['localFile'];//I('file'); //echo $file; $from = I('from', 'zh'); $to = I('to','en'); $callback = I('callback'); if(empty($file)){ $show = array('status'=>422,'message'=>'请上传图片文件'); json($show, $callback); } $path_parts = pathinfo($file); $ext = strtolower($path_parts["extension"]); $extAry = array( 'jpg','jpeg','png' ); if(!in_array($ext, $extAry)){ $show = array('status'=>423,'message'=>'请上传图片文件'); json($show, $callback); } if($from == $to){ $show = array('status'=>424,'message'=>'翻译语言不能与原文件言语一样'); json($show, $callback); } $appid = $this->appid; $salt = rand(); $cuid = 'APICUID'; $mac = 'MAC'; $secKey = $this->seckey; $imagePath = $file; //echo $imagePath; $sign = md5($appid . md5(file_get_contents($imagePath)) . $salt . $cuid . $mac . $secKey); $url = 'https://fanyi-api.baidu.com/api/trans/sdk/picture?appid=' . $this->appid . '&from=' . $from . '&to=' . $to . '&salt=' . $salt . '&cuid=' . $cuid . '&mac=' . $mac . '&sign=' . $sign; $header = array( 'Content-Type' => 'multipart/form-data', ); $sendData = array( 'image' => '@' . realpath($imagePath) . ';type=image/jpeg', ); if (class_exists('\CURLFile')) { $sendData['image'] = new \CURLFile(realpath($imagePath)); } $ch = curl_init(); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS,$sendData); curl_setopt($ch,CURLOPT_URL,$url); curl_setopt($ch,CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $result=curl_exec($ch); curl_close($ch); logs('PIC Result>>>'.$result); $callRet = json_decode($result, true); //print_R($callRet); if($callRet['error_code'] == 0){ $arr = array( 'requestId' => uniqid(), 'sourceFile' => $data['sourceFile'], 'localFile' => $file, 'type' => 'pic', 'content' => $callRet['data']['sumSrc'], 'create_time' => time() ); //print_r($arr); $this->common->set($arr); $show = array('status'=>0,'message'=>'翻译成功','srcContent'=>$callRet['data']['sumSrc'] ,'tranContent' => $callRet['data']['sumDst']); json($show, $callback); }else{ $show = array('status'=>$callRet['error_code'],'message'=>$callRet['error_msg']); json($show, $callback); } }//消息生产private function kafkaMsg($requestId){ $topicname = "testlin"; logs('kafkaMsgStart>>>' . $requestId . '>>' . $topicname); $conf = new RdKafka\Conf(); $conf->set('metadata.broker.list', 'localhost:9092'); $producer = new RdKafka\Producer($conf); $topic = $producer->newTopic($topicname); $topic->produce(RD_KAFKA_PARTITION_UA, 0, $requestId); //$producer->poll(0); $result = $producer->flush(1000); if (RD_KAFKA_RESP_ERR_NO_ERROR !== $result) { logs('ERROR>>>' . 'Was unable to flush, messages might be lost!'); } logs('kafkaMsgEnd>>>' . $requestId); }
//百度返回后的处理,通过kafka订阅消息public function dealLocal(){ $topicname = "testlin"; logs('deal start>>>'.$topicname); $conf = new RdKafka\Conf(); // Set the group id. This is required when storing offsets on the broker $conf->set('group.id', 'myConsumerGroup'); $rk = new RdKafka\Consumer($conf); $rk->addBrokers("localhost:9092"); $topicConf = new RdKafka\TopicConf(); $topicConf->set('auto.commit.interval.ms', 100); // Set the offset store method to 'file' $topicConf->set('offset.store.method', 'broker'); // Alternatively, set the offset store method to 'none' // $topicConf->set('offset.store.method', 'none'); // Set where to start consuming messages when there is no initial offset in // offset store or the desired offset is out of range. // 'earliest': start from the beginning $topicConf->set('auto.offset.reset', 'earliest'); $topic = $rk->newTopic($topicname, $topicConf); // Start consuming partition 0 $topic->consumeStart(0, RD_KAFKA_OFFSET_STORED); while (true) { $message = $topic->consume(0, 120*10000); switch ($message->err) { case RD_KAFKA_RESP_ERR_NO_ERROR: logs('MESSAGE>>>' . $message->payload); $this->dd($message->payload); break; case RD_KAFKA_RESP_ERR__PARTITION_EOF: logs( "No more messages; will wait for more\n"); break; case RD_KAFKA_RESP_ERR__TIMED_OUT: logs( "Timed out\n"); break; default: logs( $message->errstr()); //throw new \Exception($message->errstr(), $message->err); break; } }}//下载文档保存private function dd($requestId){ $app = '2020bd'; $dataEx = $this->common->getLog( array('requestId' => $requestId) ); logs('原数据ID>>>>'.json_encode($dataEx)); $fileSrcUrl = $dataEx['fileSrcUrl']; $this->common->update(array('bdfile' => $fileSrcUrl, 'status' => 1, 'update_time' => time()), array('requestId' => $requestId)); //die(); //下载文件 $sPath = '/var/www/html/activity/storage/'.$app.'/'; if(empty($fileSrcUrl)){ logs('文件不存在,结束>>>>>'); die(); } $fileTemp = explode('?', $fileSrcUrl); $path_parts = pathinfo($fileTemp[0]); $ext = strtolower($path_parts["extension"]); logs('ext:'.$ext); try{ logs('下载中>>>>'.$fileSrcUrl); $filename = date('Ymd'). '_' .rand(10000, 99999).'_'.time(); $tmpfile = $filename . '.'.$ext; $img = file_get_contents($fileSrcUrl);//logs('下载成功'.var_dump($img)); $path1 = $sPath . $tmpfile; //logs('下载中>>>>'.$path1); $res = file_put_contents($path1, $img); $_SERVER['SERVER_NAME'] = 'w.com'; if(is_https()){ $url = 'https://'.$_SERVER['SERVER_NAME'].'/storage/'.$app.'/'.$tmpfile; }else{ $url = 'http://'.$_SERVER['SERVER_NAME'].'/storage/'.$app.'/'.$tmpfile; } //if($res){ $this->common->update(array('localFile' => $url), array('requestId' => $requestId)); logs('下载成功>>>>'.$url); /*}else{ logs('下载失败'); }*/ }catch(Exception $e){ logs($e->getMessage()); } logs($requestId.'校验结束'); echo 'ok';}
//通过swoole处理,返回给前端public function dealForUser(){ $topicname = "getDocs"; logs('swoole start>>>'.$topicname); //创建WebSocket Server对象,监听0.0.0.0:9502端口 $ws = new Swoole\WebSocket\Server('0.0.0.0', 9502); //监听WebSocket连接打开事件 $ws->on('Open', function ($ws, $request) { $ws->push($request->fd, ''); }); //监听WebSocket消息事件 $ws->on('Message', function ($ws, $frame) { log("Message: {$frame->data}\n") ; $requestId = $frame->data; $file = $this->getDocs($requestId); if(!empty($file)){ $ws->push($frame->fd, "{$file}"); } }); //监听WebSocket连接关闭事件 $ws->on('Close', function ($ws, $fd) { //echo "client-{$fd} is closed\n"; }); $ws->start(); logs('swoole end>>>'.$topicname);}//读取文档内容private function getDocs($requestId){ $app = '2020bd'; $dataEx = $this->common->get_one( array('requestId' => $requestId) ); if(!empty($dataEx)){ logs('DATA FIND>>>'.$dataEx['bdfile']) ; return $dataEx['bdfile']; } return ''; logs($requestId.'校验结束'); echo 'ok';}
前端测试代码
Document Test 文件上传框显示进度条上传成功后的返回内容翻译测试: <翻译》》》> 下载文档》》》 下载 图片翻译内容>>> 翻译》》》>
转载地址:http://oned.baihongyu.com/