You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Safari浏览器上传Blob至Azure存储报fread()错误求助

Safari浏览器下Blob上传Azure存储报错:fread(): read of 8192 bytes failed with errno=21 Is a directory

我实现了Blob文件上传至Azure存储的功能,在Chrome、Firefox、Edge等浏览器均可正常运行,但在Safari中持续出现上述错误。


前端JavaScript代码

navigator.mediaDevices.getUserMedia(constraintObj)
    .then(function(mediaStreamObj) {
        // 连接媒体流到video元素
        let video = document.querySelector('video');
        if ("srcObject" in video) {
            video.srcObject = mediaStreamObj;
        } else {
            // 兼容旧版本浏览器
            video.src = window.URL.createObjectURL(mediaStreamObj);
        }
        // 录制相关监听逻辑
        let mediaRecorder = new MediaRecorder(mediaStreamObj);
        let chunks = [];

        start.addEventListener('click', (ev) => {
            $("#vid_media_recorder").prop('muted', false);
            video.play();
            mediaRecorder.start();
            console.log(mediaRecorder.state);
        })
        stop.addEventListener('click', (ev) => {
            mediaRecorder.stop();
            console.log(mediaRecorder.state);
        });
        mediaRecorder.ondataavailable = function(ev) {
            chunks.push(ev.data);
        }
        mediaRecorder.onstop = (ev) => {
            let blob = new Blob(chunks, {
                'type': 'video/mp4;'
            });
            chunks = [];
            let videoURL = window.URL.createObjectURL(blob);
            var formData = new FormData();
            formData.append('blob', blob);
            $.ajax({
                method: "POST",
                url: "media",
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                },
                data: formData,
                processData: false,
                contentType: false,
                success: function(data) {
                    media_identifier = false;
                    $("#vid_media_recorder").prop('muted', true);
                    alert(data.path_azure);
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    alert(errorThrown);
                    alert(textStatus);
                    alert(JSON.stringify(jqXHR));
                }
            });
        }
    })
    .catch(function(err) {
        console.log(err.name, err.message);
    });

前端逻辑:通过MediaRecorder录制视频生成Blob文件,停止录制后用FormData封装并通过Ajax提交至后端。


后端Laravel控制器代码

public function store(Request $request)
{
    $path = $request->file('blob')->storeAs(
        'test_capture', Str::random(16) .'.mp4', 'azure'
    );
    return response()->json([
        'success' => "Success",
        'path_azure' => $path
    ]);
}

后端逻辑:接收前端提交的Blob文件,调用storeAs方法直接上传至Azure存储。


错误指向代码

错误触发于vendor/guzzlehttp/psr7/src/Stream.php文件第228行的read方法:

public function read($length)
{
    if (!isset($this->stream)) {
        throw new \RuntimeException('Stream is detached');
    }
    if (!$this->readable) {
        throw new \RuntimeException('Cannot read from non-readable stream');
    }
    if ($length < 0) {
        throw new \RuntimeException('Length parameter cannot be negative');
    }

    if (0 === $length) {
        return '';
    }

    $string = fread($this->stream, $length);
    if (false === $string) {
        throw new \RuntimeException('Unable to read from stream');
    }

    return $string;
}

问题解决方法

1. 修复前端Blob格式与FormData提交

Safari对MIME类型的格式敏感,且提交Blob时需显式指定文件名,避免后端误判资源类型:

// 修改Blob创建时的MIME类型,移除末尾分号
let blob = new Blob(chunks, {
    type: 'video/mp4'
});
// 提交FormData时指定文件名
formData.append('blob', blob, 'recorded_video.mp4');

2. 后端增加文件有效性校验

在处理上传前先校验文件是否合法,避免处理非法资源:

public function store(Request $request)
{
    // 校验上传文件是否存在且有效
    if (!$request->hasFile('blob') || !$request->file('blob')->isValid()) {
        return response()->json(['error' => '无效的上传文件'], 400);
    }
    
    $path = $request->file('blob')->storeAs(
        'test_capture', Str::random(16) .'.mp4', 'azure'
    );
    return response()->json([
        'success' => "Success",
        'path_azure' => $path
    ]);
}

3. 手动处理Azure文件上传(备选)

若storeAs方法仍存在兼容问题,可尝试用Azure SDK手动上传文件流:

use Illuminate\Support\Facades\Storage;

public function store(Request $request)
{
    if (!$request->hasFile('blob') || !$request->file('blob')->isValid()) {
        return response()->json(['error' => '无效的上传文件'], 400);
    }

    $file = $request->file('blob');
    $client = Storage::disk('azure')->getAdapter()->getClient();
    $containerName = 'your-container-name'; // 替换为你的Azure存储容器名
    $blobName = 'test_capture/' . Str::random(16) . '.mp4';

    $client->putBlob(
        $containerName,
        $blobName,
        fopen($file->getRealPath(), 'r')
    );

    return response()->json([
        'success' => "Success",
        'path_azure' => $blobName
    ]);
}

内容的提问来源于stack exchange,提问作者shadow_blade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 06:01:17