php+nginx 下载的压缩包提示损坏

发布于 2022-09-13 00:34:05 字数 2724 浏览 22 评论 0

环境:php+nginx+华为云存储obs或阿里云oss或亚马逊等云存储产品
场景:文件在华为云,通过分片获取文件,输出到页面下载。
问题:下载的压缩文件提示损坏,小的文件没有问题,大的文件不行,从5M以上就开始提示了。起初以为是下载的文件不完整,但是看了一下文件大小,和源文件是匹配的,1G以上的文件也试过,大小相同,但是就是无法解压。
扩展:从华为云获取到的内容写入到文件,这个文件是完整的,但是直接输出到浏览器,提示损坏,因为服务器的磁盘有限和节省时间,并不打算使用nginx的sendfile功能,网上查了一下大部分是缓冲或者是nginx的临时文件问题,但是我的现象似乎并不符合。
下面贴上部分代码,写入文件的部分是测试用的,可以忽略:

<?php
    header('Content-Type: application/octet-stream');
    //声明浏览器返回大小是按字节进行计算
    header('Accept-Ranges:bytes');
    //告诉浏览器文件的总大小
    //header('Content-Length:' . $fileSize); //注意是'Content-Length:' 非Accept-Length
    //声明下载文件的名称
    header('Content-Disposition:attachment;filename=' . basename($fileInfo['path'])); //声明作为附件处理和下载后文件的名称
    #计算分片大小和需要分片的数量
    $blockSize = 5 * 1024 * 1024; // 5MB
    $blockCount = intval($objectSize / $blockSize);

    if ($objectSize % $blockSize !== 0) {
        $blockCount++;
    }
    //var_dump(compact('blockCount'));

    #开始进行分片下载
    $fp = fopen($localFilePath, 'wb');
    $promise = null;
    ob_clean();
    ob_end_clean();
    ob_implicit_flush();
    header('X-Accel-Buffering: no');
    for ($i = 0; $i < $blockCount; $i++) {
        $startPos = $i * $blockSize;
        $endPos = ($i + 1 === $blockCount) ? $objectSize - 1 : ($startPos + $blockSize - 1);
        $range = sprintf('bytes=%d-%d', $startPos, $endPos);
        //echo "range: $range;\r\n";
        $p = $obsClient->getObjectAsync([
            'Bucket' => $bucket,
            'Key' => $fileInfo['url'],
            'Range' => $range,
        ], function ($exception, $resp) use ($startPos, $i, $range, $fp) {
            fseek($fp, $startPos, 0);
            //printf("%s\n", $range);
            //printf("%s\n", $range);
            try {
                while (!$resp['Body']->eof()) {
                    $str = $resp['Body']->read(655360);
                    fwrite($fp, $str);
                    echo $str;
                    if(ob_get_level()>0){
                        ob_flush();
                        flush();
                    }
                    //sleep(1);
                    //echo "strlen:" . strlen($str) . "\r\n";
                    //echo $str;
                    //  $str = $resp ['Body']->read(65536);
                    //  fwrite($fp, $str);
                }
            } catch (Exception $exception) {
                //printf($exception);
            }
            $resp['Body']->close();
            //printf("Part#" . strval($i) . " done\n\n");
        });
        if ($promise === null) {
            $promise = $p;
        }
    }
    $promise->wait();
    fclose($fp);
    $obsClient->close();
    //  return $localFilePath;
    exit();
?>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

眼藏柔 2022-09-20 00:34:05

根据排查,基本确定了不是缓冲,nginx等问题,最终定位问题在华为云obs提供的sdk。上面代码是通过sdk中的异步获取文件内容:getObjectAsync,通过txt文档测试,直接输出,下载的文档是乱的,里面内容跟源文件顺序不一样,不清楚为什么写入文件的话,文件是对的,但是直接输出却是乱的,所以这里将获取文件的方式改成了getObject,同步获取,这样的顺序就不会乱了。下面贴一下调整后的代码:

<?php
    header('Content-Type: application/octet-stream');
    //声明浏览器返回大小是按字节进行计算
    header('Accept-Ranges:bytes');
    //告诉浏览器文件的总大小
    //header('Content-Length:' . $fileSize); //注意是'Content-Length:' 非Accept-Length
    //声明下载文件的名称
    header('Content-Disposition:attachment;filename=' . basename($fileInfo['path'])); //声明作为附件处理和下载后文件的名称
    #计算分片大小和需要分片的数量
    $blockSize = 5 * 1024 * 1024; // 5MB
    $blockCount = intval($objectSize / $blockSize);

    if ($objectSize % $blockSize !== 0) {
        $blockCount++;
    }
    //var_dump(compact('blockCount'));

    #开始进行分片下载
    // $fp = fopen($localFilePath, 'wb');
    $promise = null;
    ob_end_clean();
    ob_implicit_flush();
    header('X-Accel-Buffering: no');
    for ($i = 0; $i < $blockCount; $i++) {
        $startPos = $i * $blockSize;
        $endPos = ($i + 1 === $blockCount) ? $objectSize - 1 : ($startPos + $blockSize - 1);
        $range = sprintf('bytes=%d-%d', $startPos, $endPos);
        //echo "range: $range;\r\n";
        $reps = $obsClient->getObject([
            'Bucket' => $bucket,
            'Key' => $fileInfo['url'],
            'Range' => $range,
        ]);
        try {
            while (!$resp['Body']->eof()) {
                $str = $resp['Body']->read(655360);
                echo $str;
                if(ob_get_level()>0){
                    ob_flush();
                    flush();
                }
            }
        } catch (Exception $exception) {
            //printf($exception);
        }
        $resp['Body']->close();
    }
    $obsClient->close();
    exit();
?>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文