如何直接返回远程服务器的文件流?

914739959

问题描述

返回文件流官方说明是这个

<?php
namespace app\controller;

use support\Request;

class FooController
{
    public function hello(Request $request)
    {
        return response()->file(public_path() . '/favicon.ico');
    }
}

webman支持发送超大文件
对于大文件(超过2M),webman不会将整个文件一次性读入内存,而是在合适的时机分段读取文件并发送
webman会根据客户端接收速度来优化文件读取发送速度,保证最快速发送文件的同时将内存占用减少到最低
数据发送是非阻塞的,不会影响其它请求处理
file方法会自动添加if-modified-since头并在下一个请求时检测if-modified-since头,如果文件未修改则直接返回304以便节省带宽
发送的文件会自动使用合适的Content-Type头发送给浏览器
如果文件不存在,会自动转为404响应

如果我是要请求一个远程url,他会验证我的秘钥,然后返回一个文件流给我,我怎么返回到前端去呢,对方既不给文件名,也不返文件长度给我,就一个文件流,还不能Range断点续传。

我之前是先下载到webman服务器本地,存起来,再返给前端,但是这样大文件超过2.1GB请求会直接报错,也不知道是哪里设置的缓存太小还是怎么,报错如下:
cURL error 18: transfer closed with outstanding read data remaining (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) ;

我能否直接转发这个文件流到前端去,而不再转存到本地,这种4个GB的文件能正常转发吗?转发怎么写呢,curl获取文件流,然后 $result = curl_exec($ch),再return $result吗?

之前用guzzle下载是这样写的:

         $pdfFileResource = fopen($file_path_and_name, 'w+');
         $httpClient = new Client();
         $response = $httpClient->get($url,[RequestOptions::SINK => $pdfFileResource,'headers'=>$headers,'timeout'=>0]);
305 1 2
1个回答

walkor 打赏
<?php
namespace app\controller;
use support\Request;
use Workerman\Http\Client;
use Workerman\Protocols\Http\Chunk;

class DownloadController
{

    public function index(Request $request)
    {
        $connection = $request->connection;
        $http = new Client();
        $http->request('https://example.com/xxx.pdf', [
            'method' => 'GET',
            'progress' => function($buffer) use ($connection) {
                $connection->send(new Chunk($buffer));
            },
            'success' => function($response) use ($connection) {
                $connection->send(new Chunk('')); // 发送空的的chunk代表response结束
            },
        ]);
        return response()->withHeaders([
            "Content-Type" => "application/pdf",
            "Transfer-Encoding" => "chunked",
        ]);
    }

}

代码类似这样。
文档参考 https://www.workerman.net/doc/workerman/components/workerman-http-client.html

  • 914739959 2024-09-09
    public function downloadFile(Request $request)
    {
    
        $url = "https://api.aliyun.com/api/downfile;
    
        $headers = [
            "APPID"=>config('static.APPID'),
            "APPKEY"=>config('static.APPKEY'),
        ];
    
        $connection = $request->connection;
        $http = new \Workerman\Http\Client();
        $http->request($url, [
            'method' => 'GET',
            'progress' => function($buffer) use ($connection) {
                $connection->send(new Chunk($buffer));
            },
            'success' => function($response) use ($connection) {
                $connection->send(new Chunk('')); // 发送空的的chunk代表response结束
            },
            'headers' => $headers,
        ]);
        return response()->withHeaders([
            "Transfer-Encoding" => "chunked",
        ]);
    
    }

    这样写没用,前端点这个按钮没反应

  • aphper 2024-09-09

    6,那如果是指定部分的文件流呢,比如一个mp4文件,客户端来读取时,我取其它网站读取流来返回,mp4又可能跳跃到其他位置

  • aphper 2024-09-09

    举例可能不太恰当,现在的mp4也是像m3u8一样请求分片的,就是大概意思这种的

  • tanhongbin 2024-09-09

    你是要下载这个大文件嘛 需求明确一些

  • walkor 2024-09-09

    部分文件流前端会传来特定的http头(自己打印下查看),你把http头发给对应网站就行了

  • 914739959 2024-09-09

    对的,需求就是下载这个文件,我获取一个文件id,然后http请求一个远程地址,通过API,他会返一个文件流给我,但是有4个G,我之前都是直接下载到服务器,这个4GB的文件,我下不了,每次一到2.1G左右都报错 “cURL error 18: transfer closed with outstanding read data remaining” ,所以我才想能不能直接转发这个流给前端,但是有点不好处理,网上的案例都是远程地址是个 xxx.com/xxx/xxx.pdf ,这种资源文件地址,但我这个其实是个API接口,他通过验证才会返文件流

×
🔝