ExportZip2.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. <?php
  2. namespace App\Console\Commands;
  3. use Illuminate\Console\Command;
  4. use Illuminate\Support\Facades\Storage;
  5. use Illuminate\Support\Facades\Log;
  6. use Illuminate\Support\Facades\Cache;
  7. use Illuminate\Support\Facades\App;
  8. use Symfony\Component\Process\Process;
  9. class ExportZip2 extends Command
  10. {
  11. protected $signature = 'export:zip2
  12. {filename : filename}
  13. {title : title}
  14. {id : 标识符}
  15. {format? : zip file format 7z,lzma,gz }';
  16. protected $description = '压缩导出的文件';
  17. public function handle()
  18. {
  19. Log::debug('export offline: 开始压缩');
  20. $defaultExportPath = storage_path('app/public/export/offline');
  21. $exportFile = $this->argument('filename');
  22. $filename = basename($exportFile);
  23. if ($filename === $exportFile) {
  24. $exportFullFileName = $defaultExportPath . '/' . $filename;
  25. $exportPath = $defaultExportPath;
  26. } else {
  27. $exportFullFileName = $exportFile;
  28. $exportPath = dirname($exportFile);
  29. }
  30. $format = $this->argument('format') ?? 'gz';
  31. if (!file_exists($exportFullFileName)) {
  32. Log::error('export offline: file not exists', [
  33. 'file' => $exportFullFileName
  34. ]);
  35. $this->error('file not exists: ' . $exportFullFileName);
  36. return 1;
  37. }
  38. $zipFile = $this->getZipFileName($filename, $format);
  39. $zipFullFileName = $exportPath . '/' . $zipFile;
  40. if (file_exists($zipFullFileName)) {
  41. unlink($zipFullFileName);
  42. }
  43. $this->info("start compress: {$exportFullFileName}");
  44. Log::debug('export offline zip start', [
  45. 'file' => $exportFullFileName,
  46. 'format' => $format
  47. ]);
  48. $this->compress($exportFullFileName, $zipFullFileName, $format);
  49. $this->info('压缩完成');
  50. Log::debug('zip done', [
  51. 'zip' => $zipFullFileName
  52. ]);
  53. /*
  54. |--------------------------------------------------------------------------
  55. | 上传 S3
  56. |--------------------------------------------------------------------------
  57. */
  58. $bucket = config('mint.attachments.bucket_name.temporary');
  59. $tmpFile = $bucket . '/' . $zipFile;
  60. $this->info('upload file=' . $tmpFile);
  61. Log::debug('export offline upload', [
  62. 'file' => $tmpFile
  63. ]);
  64. Storage::put($tmpFile, fopen($zipFullFileName, 'r'));
  65. $this->info('upload done');
  66. Log::debug('upload done');
  67. /*
  68. |--------------------------------------------------------------------------
  69. | 生成下载链接
  70. |--------------------------------------------------------------------------
  71. */
  72. if (App::environment('local')) {
  73. $link = Storage::url($tmpFile);
  74. } else {
  75. try {
  76. $link = Storage::temporaryUrl(
  77. $tmpFile,
  78. now()->addDays(2)
  79. );
  80. } catch (\Exception $e) {
  81. Log::error('temporaryUrl fail', [
  82. 'exception' => $e
  83. ]);
  84. $this->error('generate temporaryUrl fail');
  85. return 1;
  86. }
  87. }
  88. $this->info('link=' . $link);
  89. /*
  90. |--------------------------------------------------------------------------
  91. | CDN 列表
  92. |--------------------------------------------------------------------------
  93. */
  94. $url = [];
  95. foreach (config('mint.server.cdn_urls') as $key => $cdn) {
  96. $url[] = [
  97. 'link' => $cdn . '/' . $zipFile,
  98. 'hostname' => 'china cdn-' . $key
  99. ];
  100. }
  101. $url[] = [
  102. 'link' => $link,
  103. 'hostname' => 'Amazon cloud storage(Hongkong)'
  104. ];
  105. /*
  106. |--------------------------------------------------------------------------
  107. | Cache 写入
  108. |--------------------------------------------------------------------------
  109. */
  110. $info = Cache::get('/offline/index', []);
  111. $info[] = [
  112. 'id' => $this->argument('id'),
  113. 'title' => $this->argument('title'),
  114. 'filename' => $zipFile,
  115. 'url' => $url,
  116. 'create_at' => now()->toDateTimeString(),
  117. 'chapter' => Cache::get("/export/chapter/count"),
  118. 'filesize' => filesize($zipFullFileName),
  119. 'min_app_ver' => '1.3',
  120. ];
  121. Cache::put('/offline/index', $info);
  122. /*
  123. |--------------------------------------------------------------------------
  124. | 删除原始文件
  125. |--------------------------------------------------------------------------
  126. */
  127. sleep(5);
  128. try {
  129. if (is_file($exportFullFileName)) {
  130. unlink($exportFullFileName);
  131. }
  132. if (file_exists($zipFullFileName)) {
  133. unlink($zipFullFileName);
  134. }
  135. } catch (\Throwable $e) {
  136. Log::error('delete source fail', [
  137. 'exception' => $e
  138. ]);
  139. }
  140. return 0;
  141. }
  142. /*
  143. |--------------------------------------------------------------------------
  144. | 生成压缩文件名
  145. |--------------------------------------------------------------------------
  146. */
  147. protected function getZipFileName(string $filename, string $format): string
  148. {
  149. return match ($format) {
  150. '7z' => $filename . '.7z',
  151. 'lzma' => $filename . '.lzma',
  152. default => $filename . '.tar.gz'
  153. };
  154. }
  155. /*
  156. |--------------------------------------------------------------------------
  157. | 压缩函数
  158. |--------------------------------------------------------------------------
  159. */
  160. protected function compress($source, $target, $format)
  161. {
  162. $isDir = is_dir($source);
  163. switch ($format) {
  164. case '7z':
  165. $command = [
  166. '7z',
  167. 'a',
  168. '-t7z',
  169. '-mx=9',
  170. $target,
  171. $source
  172. ];
  173. break;
  174. case 'lzma':
  175. if ($isDir) {
  176. $tmpTar = $source . '.tar';
  177. $tar = new Process([
  178. 'tar',
  179. '-cf',
  180. $tmpTar,
  181. '-C',
  182. dirname($source),
  183. basename($source)
  184. ]);
  185. $tar->run();
  186. $source = $tmpTar;
  187. }
  188. $command = [
  189. 'xz',
  190. '-k',
  191. '-9',
  192. '--format=lzma',
  193. $source
  194. ];
  195. break;
  196. default:
  197. $command = [
  198. 'tar',
  199. '-czf',
  200. $target,
  201. '-C',
  202. dirname($source),
  203. basename($source)
  204. ];
  205. }
  206. $this->info(implode(' ', $command));
  207. $process = new Process($command);
  208. $process->setTimeout(60 * 60 * 6);
  209. $process->run();
  210. $this->info($process->getOutput());
  211. if (!$process->isSuccessful()) {
  212. Log::error('compress fail', [
  213. 'error' => $process->getErrorOutput()
  214. ]);
  215. throw new \RuntimeException($process->getErrorOutput());
  216. }
  217. }
  218. }