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

Flutter audioplayers插件在iPhone/Web端无法获取音频时长

问题根因

iOS全系列浏览器、桌面Safari(包括Flutter Web在这些环境的运行时,基于WebKit内核)加载音频时,不会先下载完整文件,会先发起小范围的HTTP Range请求,只读取音频头部的元数据块来计算时长。
你当前的代理逻辑没有正确实现标准Range响应规则:无论客户端是否发起Range请求,都固定返回全量文件+强制写入覆盖全文件的Content-Range头,不符合WebKit内核对分段响应的校验规则,内核无法正确读取元数据,自然拿不到音频时长。安卓、桌面Chrome等环境容错性更强,即使Range响应不规范也能通过下载部分文件解析时长,所以不会复现问题。

修复步骤
  • 移除无条件返回全量Content-Range头的逻辑,仅在客户端携带Range请求头时返回分段响应
  • 分段请求场景下必须返回206 Partial Content状态码,Content-Length设置为当前返回分段的实际长度,而非整个文件的大小
  • 非分段的普通请求场景下,返回200 OK状态码+完整文件长度,不要携带Content-Range头
  • 确保返回Accept-Ranges: bytes头,明确告知客户端服务端支持分段请求
修正后参考代码
function printAudio($audioID)
{
    $s3FileUrl = "my_file_url";
    header_remove();

    // 建议替换为AWS S3 SDK的headObject方法获取源文件信息,比curl/GET请求效率更高
    $fileMeta = getS3FileMeta($s3FileUrl);
    $fileSize = $fileMeta['size'];
    $mimeType = $fileMeta['mime_type'];
    $etag = $fileMeta['etag'];
    $lastModified = $fileMeta['last_modified'];

    // 公共响应头
    header("Accept-Ranges: bytes");
    header("Content-Type: " . $mimeType);
    header("ETag: " . $etag);
    header("Last-Modified: " . $lastModified);

    $rangeHeader = $_SERVER['HTTP_RANGE'] ?? '';
    if (preg_match('/^bytes=(\d*)-(\d*)$/', trim($rangeHeader), $rangeMatch)) {
        $start = $rangeMatch[1] === '' ? 0 : (int)$rangeMatch[1];
        $end = $rangeMatch[2] === '' ? $fileSize - 1 : (int)$rangeMatch[2];
        $end = min($end, $fileSize - 1);

        if ($start > $end) {
            header("HTTP/1.1 416 Requested Range Not Satisfiable");
            header("Content-Range: bytes */$fileSize");
            exit;
        }

        $segmentLength = $end - $start + 1;
        header("HTTP/1.1 206 Partial Content");
        header("Content-Length: " . $segmentLength);
        header("Content-Range: bytes $start-$end/$fileSize");
        // 仅输出请求的分段内容,不要返回整个文件
        outputFileSegment($s3FileUrl, $start, $segmentLength);
    } else {
        header("HTTP/1.1 200 OK");
        header("Content-Length: " . $fileSize);
        readfile($s3FileUrl);
    }
    exit;
}

// 获取S3源文件元信息,建议替换为S3 SDK对应方法
function getS3FileMeta($url) {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_NOBODY => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HEADER => true,
        CURLOPT_FOLLOWLOCATION => true
    ]);
    curl_exec($ch);
    $info = curl_getinfo($ch);
    curl_close($ch);
    return [
        'size' => $info['download_content_length'],
        'mime_type' => $info['content_type'],
        'etag' => $info['etag'] ?? '',
        'last_modified' => $info['last_modified'] ?? gmdate('D, d M Y H:i:s') . ' GMT'
    ];
}

// 输出文件指定分段内容
function outputFileSegment($url, $start, $length) {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RANGE => "$start-" . ($start + $length - 1),
        CURLOPT_BINARYTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true
    ]);
    curl_exec($ch);
    curl_close($ch);
}
额外可选方案

如果不想自行维护代理层的Range请求逻辑,可以直接生成S3预签名URL传给audioplayers插件,S3原生符合Range请求规范,不需要额外处理即可在所有环境正常获取音频时长。

注意:HTTP响应头本身大小写不敏感,之前尝试修改头的大小写没有效果是正常的,不需要在这部分浪费时间。

内容的提问来源于stack exchange,提问作者Adrián Pastoriza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 05:33:23