Laravel集成Tus协议结合Uppy上传时的异常问题求助
Laravel集成Tus协议结合Uppy上传时的异常问题求助
各位好,我目前在Laravel项目中尝试通过Tus协议搭配Uppy实现断点续传上传功能,但遇到了一系列异常问题,具体表现如下:
- 上传文件时,服务器始终接收不到文件数据
- 初始的HEAD请求返回404错误,提示文件不存在
- POST请求发送后,服务器确实创建了对应的文件和.info信息文件,但后续的PATCH请求依旧提示文件找不到
- PATCH请求中获取到的
Upload-Length值为0,浏览器开发者工具里也能看到相同的异常
以下是我编写的FileUploadController代码,麻烦各位帮忙排查下问题所在:
<?php class FileUploadController extends Controller { private int $maxSize = 50000000000; // 50 GO private string $disk = 'local'; private string $uploadPath = 'uploads'; public function options(): Response { return response('', 204) ->header('Vary', 'Origin') ->header('Access-Control-Allow-Origin', '*') ->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, HEAD, PATCH') ->header('Access-Control-Allow-Headers', 'Tus-Resumable, Upload-Length, Upload-Metadata, Upload-Offset, Content-Type, Location, Authorization') ->header('Access-Control-Expose-Headers', 'Upload-Offset, Upload-Length, Location, Tus-Resumable, Upload-Metadata') ->header('Access-Control-Max-Age', '86400') ->header('Tus-Resumable', '1.0.0',) ->header('Tus-Version', '1.0.0',) ; } public function getFileResource( Request $request, string $fileId ): Response { $infoPath = $this->uploadPath . '/' . $fileId . '.info'; $storage = Storage::disk($this->disk); if (!$storage->exists($infoPath)) { return response('Not found', Response::HTTP_NOT_FOUND) ->header('Access-Control-Allow-Origin', '*'); } $info = json_decode($storage->get($infoPath), true); $length = $info['length']; return response('', 200) ->header('Access-Control-Allow-Origin', '*') ->header('Access-Control-Allow-Headers', 'Tus-Resumable, Upload-Length, Upload-Metadata, Upload-Offset, Content-Type, Location, Authorization') ->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, HEAD, PATCH') ->header('Access-Control-Expose-Headers', 'Upload-Offset, Upload-Length, Location, Tus-Resumable, Upload-Metadata') ->header('Tus-Resumable', '1.0.0') ->header('Upload-Length', $length) ->header('Location', 'https://hopla.api.xefi.local.xyz/api/upload/' . $fileId) ; } public function createFileResource( Request $request ): Response { $log = "=== POST ===\n"; $log .= "HEADERS: " . var_export(getallheaders(), true) . "\n"; $log .= "Upload-Length: " . ($_SERVER['HTTP_UPLOAD_LENGTH'] ?? 'N/A') . "\n"; $log .= "Content-Length: " . ($_SERVER['CONTENT_LENGTH'] ?? 'N/A') . "\n"; \Log::debug($log); $storage = Storage::disk($this->disk); if (!$storage->exists($this->uploadPath)) { $storage->makeDirectory($this->uploadPath); } $uploadLength = $request->header('Upload-Length'); $uploadMetadata = $request->header('Upload-Metadata', ''); $fileId = 'upload_' . \Str::uuid(); $storage->put($this->uploadPath . '/' . $fileId, ''); $storage->put($this->uploadPath . '/' . $fileId . '.info', json_encode([ 'offset' => 0, 'length' => $uploadLength, 'metadata' => $uploadMetadata, ])); return response('', Response::HTTP_CREATED) ->header('Access-Control-Allow-Origin', '*') ->header('Access-Control-Allow-Headers', 'Tus-Resumable, Upload-Length, Upload-Metadata, Upload-Offset, Content-Type, Location, Authorization') ->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, HEAD, PATCH') ->header('Access-Control-Expose-Headers', 'Upload-Offset, Upload-Length, Location, Tus-Resumable, Upload-Metadata') ->header('Tus-Resumable', '1.0.0') ->header('Location', 'https://hopla.api.xefi.local.xyz/api/upload/' . $fileId) ; } public function uploadFileChunk( Request $request, string $fileId ): Response { $storage = Storage::disk('local'); $infoPath = $this->uploadPath . '/' . $fileId . '.info'; $filePath = storage_path('app/' . $this->uploadPath . '/' . $fileId . '.part'); if (!$storage->exists($infoPath)) { return response('File not found', Response::HTTP_NOT_FOUND); } $info = json_decode($storage->get($infoPath), true); $uploadOffset = (int) $request->header('Upload-Offset', 0); $currentOffset = $info['offset'] ?? 0; if ($uploadOffset !== $currentOffset) { return response('Offset mismatch', Response::HTTP_CONFLICT); } $inputStream = fopen('php://input', 'rb'); $fileStream = fopen($filePath, 'c+b'); fseek($fileStream, $uploadOffset); $written = stream_copy_to_stream($inputStream, $fileStream); fclose($inputStream); fclose($fileStream); $info['offset'] += $written; $storage->put($infoPath, json_encode($info)); return response('', Response::HTTP_NO_CONTENT) ->header('Vary', 'Origin') ->header('Access-Control-Allow-Origin', '*') ->header('Access-Control-Allow-Headers', 'Tus-Resumable, Upload-Length, Upload-Metadata, Upload-Offset, Content-Type, Location, Authorization') ->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, HEAD, PATCH') ->header('Access-Control-Expose-Headers', 'Upload-Offset, Upload-Length, Location, Tus-Resumable, Upload-Metadata') ; } }
我自己初步排查了几个点,但还是没找到问题根源:
- 已经在OPTIONS请求中配置了完整的CORS头,跨域相关的响应头应该没有问题
- POST请求的日志里能正确获取到
Upload-Length,说明初始请求的参数是正常的 - 检查过本地存储路径,POST请求确实创建了对应的空文件和.info信息文件,但后续HEAD、PATCH请求似乎无法正确定位到这些文件
希望有经验的大佬能帮忙分析下问题出在哪里,谢谢!
内容来源于stack exchange
相关产品推荐
相关产品推荐

