ExportDownload.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. <?php
  2. namespace App\Tools;
  3. use Illuminate\Support\Str;
  4. use Illuminate\Support\Facades\Log;
  5. use Illuminate\Support\Facades\Storage;
  6. use Illuminate\Support\Facades\App;
  7. use Symfony\Component\Process\Process;
  8. use Symfony\Component\Process\Exception\ProcessFailedException;
  9. use App\Tools\RedisClusters;
  10. use App\Tools\Export;
  11. class ExportDownload
  12. {
  13. protected $statusKey = 'export/status';
  14. protected $statusExpiry = 3600;
  15. protected $currStatusKey = '';
  16. protected $queryId = 'id';
  17. protected $realFilename = 'index';//压缩包里的文件名
  18. protected $zipFilename = 'file.zip';
  19. protected $downloadUrl = null;
  20. protected $format = 'tex';
  21. protected $debug = false;
  22. protected $logs = [];
  23. /**
  24. * Create a new command instance.
  25. *
  26. * @return void
  27. */
  28. public function __construct($options=[])
  29. {
  30. if(isset($options['format'])){
  31. $this->format = $options['format'];
  32. }
  33. if(isset($options['debug'])){
  34. $this->debug = $options['debug'];
  35. }
  36. if(isset($options['filename'])){
  37. $this->realFilename = $options['filename'];
  38. }
  39. $this->queryId = $options['queryId'];
  40. $this->zipFilename = $this->queryId.'.zip';
  41. $this->currStatusKey = $this->statusKey . '/' . $this->queryId;
  42. }
  43. /**
  44. * progress: 0-1, error -1
  45. * message: string
  46. */
  47. public function setStatus($progress,$message=''){
  48. $this->logs[] = $message;
  49. $data = [
  50. 'progress'=>$progress,
  51. 'message'=>$message,
  52. 'log'=>$this->logs,
  53. 'content-type'=>'application/zip',
  54. ];
  55. if($this->downloadUrl){
  56. $data['url'] = $this->downloadUrl;
  57. }
  58. RedisClusters::put($this->currStatusKey,
  59. $data,
  60. $this->statusExpiry);
  61. $percent = (int)($progress * 100);
  62. return "[{$percent}%]".$message;
  63. }
  64. public function getStatus(){
  65. return RedisClusters::get($this->currStatusKey);
  66. }
  67. public function upload(string $type,$sections,$bookMeta){
  68. $outputFilename = Str::uuid();
  69. $m = new \Mustache_Engine(array('entity_flags'=>ENT_QUOTES,
  70. 'delimiters' => '[[ ]]',
  71. 'escape'=>function ($value){
  72. return $value;
  73. }));
  74. $tex = array();
  75. $_format = 'md';
  76. $tplFile = resource_path("mustache/".$type.'/'.$_format."/main.".$_format);
  77. $tpl = file_get_contents($tplFile);
  78. $texContent = $m->render($tpl,$bookMeta);
  79. $tex[] = [
  80. 'name' => 'main.'.$_format,
  81. 'content' => $texContent
  82. ];
  83. foreach ($sections as $key => $section) {
  84. $tplFile = resource_path("mustache/".$type.'/'.$_format."/section.".$_format);
  85. $tpl = file_get_contents($tplFile);
  86. $texContent = $m->render($tpl,$section['body']);
  87. $tex[] = [
  88. 'name'=>$section['name'],
  89. 'content'=>$texContent
  90. ];
  91. }
  92. Log::debug('footnote start');
  93. //footnote
  94. $tplFile = resource_path("mustache/".$_format."/footnote.".$_format);
  95. if(isset($GLOBALS['note']) &&
  96. is_array($GLOBALS['note']) &&
  97. count($GLOBALS['note'])>0 &&
  98. file_exists($tplFile)){
  99. $tpl = file_get_contents($tplFile);
  100. $texContent = $m->render($tpl,['footnote'=>$GLOBALS['note']]);
  101. $tex[] = [
  102. 'name'=>'footnote.'.$_format,
  103. 'content'=>$texContent
  104. ];
  105. }
  106. Log::debug('footnote finished');
  107. $this->setStatus(0.95,'export content done. tex count='.count($tex));
  108. Log::debug('export content done.',['tex_count'=>count($tex)]);
  109. //upload
  110. $fileDate = '';
  111. switch ($this->format) {
  112. case 'tex':
  113. $data = Export::ToPdf($tex);
  114. if($data['ok']){
  115. $this->info($data['content-type']);
  116. $fileDate = $data['data'];
  117. }else{
  118. $this->error($data['code'].'-'.$data['message']);
  119. }
  120. break;
  121. default:
  122. $file = array();
  123. foreach ($tex as $key => $section) {
  124. $file[] = $section['content'];
  125. }
  126. $fileDate = implode('',$file);
  127. break;
  128. }
  129. $dir = "tmp/export/{$type}/".$this->format."/";
  130. $mdFilename = $dir.$outputFilename.'.md';
  131. Storage::disk('local')->put($mdFilename, $fileDate);
  132. Log::debug('markdown saved',['filename'=>$mdFilename]);
  133. if($this->format === 'markdown'){
  134. $filename = $mdFilename;
  135. }else{
  136. $filename = $dir.$outputFilename.'.'.$this->format;
  137. Log::debug('tmp saved',['filename'=>$filename]);
  138. $absoluteMdPath = Storage::disk('local')->path($mdFilename);
  139. $absoluteOutputPath = Storage::disk('local')->path($filename);
  140. //$command = "pandoc pandoc1.md --reference-doc tpl.docx -o pandoc1.docx";
  141. $command = ['pandoc', $absoluteMdPath, '-o', $absoluteOutputPath];
  142. if($this->format === 'docx'){
  143. $tplFile = resource_path("template/docx/paper.docx");
  144. array_push($command,'--reference-doc');
  145. array_push($command,$tplFile);
  146. }
  147. Log::debug('pandoc start',['command'=>$command,'format'=>$this->format]);
  148. $process = new Process($command);
  149. $process->run();
  150. if (!$process->isSuccessful()) {
  151. throw new ProcessFailedException($process);
  152. }
  153. echo $process->getOutput();
  154. Log::debug('pandoc end',['command'=>$command]);
  155. }
  156. $zipDir = storage_path('app/export/zip');
  157. if(!is_dir($zipDir)){
  158. $res = mkdir($zipDir,0755,true);
  159. if(!$res){
  160. Log::error('mkdir fail path='.$zipDir);
  161. return 1;
  162. }
  163. }
  164. $zipFile = $zipDir.'/'. $outputFilename .'.zip';
  165. Log::debug('export chapter start zip file='.$zipFile);
  166. //zip压缩包里面的文件名
  167. $realFilename = $this->realFilename.".".$this->format;
  168. $fileContent = Storage::disk('local')->get($filename);
  169. $zipOk = \App\Tools\Tools::zip($zipFile,[$realFilename=>$fileContent]);
  170. if(!$zipOk){
  171. Log::error('export chapter zip fail zip file='.$zipFile);
  172. $this->setStatus(0.99,'export chapter zip fail');
  173. $this->error('export chapter zip fail zip file='.$zipFile);
  174. //TODO 给客户端返回错误状态
  175. return 1;
  176. }
  177. $this->setStatus(0.96,'export chapter zip success');
  178. $bucket = config('mint.attachments.bucket_name.temporary');
  179. $tmpFile = $bucket.'/'. $this->zipFilename ;
  180. Log::debug('upload start filename='.$tmpFile);
  181. $this->setStatus(0.97,'upload start ');
  182. $zipData = file_get_contents($zipFile);
  183. Storage::put($tmpFile, $zipData);
  184. if (App::environment('local')) {
  185. $s3Link = Storage::url($tmpFile);
  186. }else{
  187. try{
  188. $s3Link = Storage::temporaryUrl($tmpFile, now()->addDays(7));
  189. }catch(\Exception $e){
  190. Log::error('export {Exception}',['exception'=>$e]);
  191. return false;
  192. }
  193. }
  194. $this->downloadUrl = $s3Link;
  195. $this->setStatus(1,'export chapter done');
  196. Log::debug('export chapter done, upload',['filename'=>$tmpFile,'url'=>$s3Link] );
  197. unlink($zipFile);
  198. return true;
  199. }
  200. }