UploadController.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. <?php
  2. /**
  3. * 大文件分片上传示例控制器(webman 1.x / 2.x 通用,本地与云端通用)
  4. *
  5. * 使用方式:将本文件复制到 webman 项目的 app/controller/ 目录,
  6. * 并按 example/route.php 注册路由。
  7. *
  8. * 所有接口通过 disk 参数选择存储(默认 local):local / oss / cos / qiniu / s3,
  9. * 需在 config/plugin/tinywan/storage/app.php 配置对应 disk 后使用。
  10. */
  11. declare(strict_types=1);
  12. namespace app\controller;
  13. use Tinywan\Storage\Storage;
  14. use Webman\Http\Request;
  15. class UploadController
  16. {
  17. /**
  18. * 允许的 disk 白名单
  19. *
  20. * @var string[]
  21. */
  22. private const DISKS = [
  23. Storage::MODE_LOCAL,
  24. Storage::MODE_OSS,
  25. Storage::MODE_COS,
  26. Storage::MODE_QINIU,
  27. Storage::MODE_S3,
  28. ];
  29. /**
  30. * 上传单个分片
  31. * POST /upload/chunk form-data: file(分片文件)、file_id、chunk_index、chunk_md5、disk(可选)
  32. */
  33. public function chunk(Request $request)
  34. {
  35. $file = $request->file('file');
  36. if (!$file) {
  37. return json(['code' => 400, 'msg' => '未找到分片文件'], 400);
  38. }
  39. try {
  40. $data = $this->disk($request->post('disk', Storage::MODE_LOCAL))->uploadChunk(
  41. $file,
  42. (string) $request->post('file_id', ''),
  43. (int) $request->post('chunk_index', 0),
  44. (string) $request->post('chunk_md5', ''),
  45. );
  46. return json(['code' => 0, 'msg' => 'ok', 'data' => $data]);
  47. } catch (\Throwable $e) {
  48. return json(['code' => 500, 'msg' => $e->getMessage()], 500);
  49. }
  50. }
  51. /**
  52. * 断点续传进度查询
  53. * GET /upload/progress?file_id=xxx&total_chunks=3&disk=local
  54. */
  55. public function progress(Request $request)
  56. {
  57. try {
  58. $data = $this->disk($request->get('disk', Storage::MODE_LOCAL))->chunkProgress(
  59. (string) $request->get('file_id', ''),
  60. (int) $request->get('total_chunks', 0),
  61. );
  62. return json(['code' => 0, 'msg' => 'ok', 'data' => $data]);
  63. } catch (\Throwable $e) {
  64. return json(['code' => 500, 'msg' => $e->getMessage()], 500);
  65. }
  66. }
  67. /**
  68. * 合并全部分片
  69. * POST /upload/merge form-data: file_id、total_chunks、file_name(完整文件名,如 video.mp4)、disk(可选)
  70. *
  71. * 云端合并建议传 file_hash(整文件散列)作为最终对象名;同时可传 mime_type
  72. * (云端无法探测 MIME,否则默认 application/octet-stream)
  73. */
  74. public function merge(Request $request)
  75. {
  76. try {
  77. $options = [];
  78. $fileHash = (string) $request->post('file_hash', '');
  79. if ('' !== $fileHash) {
  80. $options['file_hash'] = $fileHash;
  81. }
  82. $mimeType = (string) $request->post('mime_type', '');
  83. if ('' !== $mimeType) {
  84. $options['mime_type'] = $mimeType;
  85. }
  86. $data = $this->disk($request->post('disk', Storage::MODE_LOCAL))->mergeChunk(
  87. (string) $request->post('file_id', ''),
  88. (int) $request->post('total_chunks', 0),
  89. (string) $request->post('file_name', ''),
  90. $options,
  91. );
  92. return json(['code' => 0, 'msg' => 'ok', 'data' => $data]);
  93. } catch (\Throwable $e) {
  94. return json(['code' => 500, 'msg' => $e->getMessage()], 500);
  95. }
  96. }
  97. /**
  98. * 按参数解析存储 disk,非法值抛异常
  99. */
  100. private function disk(string $disk)
  101. {
  102. if (!in_array($disk, self::DISKS, true)) {
  103. throw new \InvalidArgumentException('不支持的 disk:' . $disk);
  104. }
  105. return Storage::disk($disk, false);
  106. }
  107. }