JavaScript+Laravel实现大文件断点续传断网后fopen权限报错怎么解决
问题根因
- 核心原因不是真的权限不足,而是Windows系统下多个请求同时写入同一个分片临时文件时,系统会给正在写入的文件加独占锁,后续请求再操作这个文件就会抛出权限拒绝错误。你正常网络下不会出现并发写同一个分片的情况,所以不会报错,断网重试时就会触发这个问题。
- 前端配置
testChunks: false关闭了分片存在性检测,断网恢复后续传时,已经上传过的分片会被重复上传,直接触发并发写冲突。 - 断网时你只是弹出提示,没有暂停上传队列,resumable会按照你配置的重试规则不断发起请求,多个重试请求会同时请求同一个分片的写入接口。
修复方案
前端修改
- 开启分片检测,将resumable配置中的
testChunks: false改为testChunks: true,resumable会在上传每个分片前先检测该分片是否已经上传成功,已成功的分片会直接跳过,避免重复写入。 - 优化断网处理逻辑,监听到网络断开时暂停上传,网络恢复后再继续:
resumable.on('fileProgress', function (file) { if(navigator.onLine === true){ updateProgress(Math.floor(file.progress() * 100)); // 网络恢复后如果是暂停状态就重启上传 if(resumable.isPaused()){ resumable.upload() } } else{ alert('网络已断开,上传暂停'); // 暂停上传队列,避免无意义重试 resumable.pause() } });
- 调整重试规则,仅针对网络错误类的响应重试,避免业务错误无意义重试:
resumable.on('fileError', function (file, response) { console.log(response); // 如果是503类的服务端临时不可用错误再重试,其他错误可以终止上传给出提示 if(response.status !== 503){ alert('上传出错,请重新选择文件') resumable.cancel() } });
后端修改
- 临时文件路径统一使用Laravel辅助函数生成,避免路径分隔符混乱。你当前报错路径中同时存在Windows的
\\和Linux的/分隔符,可以在配置filesystems时统一用storage_path('app/products')生成路径。 - 增加文件写锁判断,接收分片时先检测文件是否被占用,如果被占用直接返回503让前端稍后重试:
public function uploadLargeFiles(Request $request) { $receiver = new FileReceiver('file', $request, HandlerFactory::classFromRequest($request)); if (!$receiver->isUploaded()) { return response()->json(['status' => false, 'message' => '文件未上传'], 400); } try { $fileReceived = $receiver->receive(); } catch (\ErrorException $e) { // 捕获文件打开失败的错误,返回503让前端重试 if(str_contains($e->getMessage(), 'Permission denied') || str_contains($e->getMessage(), 'Failed to open stream')){ return response()->json(['status' => false, 'message' => '临时文件占用'], 503); } throw $e; } if ($fileReceived->isFinished()) { $file = $fileReceived->getFile(); $extension = $file->getClientOriginalExtension(); $fileName = str_replace('.'.$extension, '', $file->getClientOriginalName()); $fileName .= '_' . md5(time()) . '.' . $extension; $disk = Storage::disk(config('filesystems.default')); $path = $disk->putFileAs('products', $file, $fileName); unlink($file->getPathname()); return [ 'path' => asset('storage/' . $path), 'filename' => $fileName ]; } $handler = $fileReceived->handler(); return [ 'done' => $handler->getPercentageDone(), 'status' => true ]; }
修改完成后可测试断网场景:上传一半断开网络,10秒后再恢复,验证是否可以正常续传完成。
内容的提问来源于stack exchange,提问作者Codinoz Technologies
相关产品推荐
相关产品推荐

