简体   繁体   English

使用指向外部资源的 URL 在 Laravel 中下载文件

[英]Download a file in Laravel using a URL to external resource

I'm keeping all uploads on a custom, external drive.我将所有上传内容保存在自定义的外部驱动器上。 The files are being stored via custom API.这些文件是通过自定义 API 存储的。

In Laravel 5.2, I can do this for a local file to download it:在 Laravel 5.2 中,我可以对本地文件执行此操作以下载它:

return response()->download('path/to/file/image.jpg');

Unfortunately, when I pass a URL instead of a path, Laravel throws an error:不幸的是,当我传递 URL 而不是路径时,Laravel 会抛出错误:

The file " https://my-cdn.com/files/image.jpg " does not exist文件“ https://my-cdn.com/files/image.jpg ”不存在

(the URL is a dummy of course). (URL当然是一个虚拟的)。

Is there any way I can download the image.jpg file using Laravel's implementation or do I do this with plain PHP instead?有什么方法可以使用 Laravel 的实现来下载image.jpg文件,还是用普通的 PHP 来做呢?

There's no magic, you should download external image using copy() function, then send it to user in the response:没有魔法,您应该使用copy()函数下载外部图像,然后在响应中将其发送给用户:

$filename = 'temp-image.jpg';
$tempImage = tempnam(sys_get_temp_dir(), $filename);
copy('https://my-cdn.com/files/image.jpg', $tempImage);

return response()->download($tempImage, $filename);

TL;DR TL;博士
Use the streamDownload response if you're using 5.6 or greater.如果您使用的是 5.6 或更高版本,请使用streamDownload 响应 Otherwise implement the function below.否则执行下面的函数。

Original Answer原始答案
Very similar to the "download" response, Laravel has a "stream" response which can be used to do this.与“下载”响应非常相似,Laravel 有一个“流”响应可用于执行此操作。 Looking at the API , both of the these functions are wrappers around Symfony's BinaryFileResponse and StreamedResponse classes.查看API ,这两个函数都是 Symfony 的 BinaryFileResponse 和 StreamedResponse 类的包装器。 In the Symfony docs they have good examples of how to create a StreamedResponse在 Symfony 文档中,他们有很好的例子来说明如何创建 StreamedResponse

Below is my implementation using Laravel:下面是我使用 Laravel 的实现:

<?php

use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

Route::get('/', function () {
    $response = response()->stream(function () {
        echo file_get_contents('http://google.co.uk');
    });

    $name = 'index.html';

    $disposition = $response->headers->makeDisposition(
        ResponseHeaderBag::DISPOSITION_ATTACHMENT,
        $name,
        str_replace('%', '', Str::ascii($name))
    );

    $response->headers->set('Content-Disposition', $disposition);

    return $response;
});

Update 2018-01-17 2018-01-17 更新

This has now been merged into Laravel 5.6 and has been added to the 5.6 docs .这现在已经被合并到 Laravel 5.6中,并被添加到5.6 文档中。 The streamDownload response can be called like this:可以像这样调用 streamDownload 响应:

<?php

Route::get('/', function () {
    return response()->streamDownload(function () {
        echo file_get_contents('https://my.remote.com/file/store-12345.jpg');
    }, 'nice-name.jpg');
});

As for 2020 this is all pretty easy.至于2020年,这一切都很容易。

2 lines of code to download to your server and 2 lines to upload to browser (if needed). 2 行代码下载到您的服务器,2 行代码上传到浏览器(如果需要)。

Assume you want an HTML for google home page to get saved locally onto your server, and then return an HTTP response to initiate a browser to download the file at client slide:假设您希望将 Google 主页的 HTML 本地保存到您的服务器上,然后返回 HTTP 响应以启动浏览器以在客户端幻灯片上下载文件:

// Load the file contents into a variable.
$contents = file_get_contents('www.google.com');

// Save the variable as `google.html` file onto
// your local drive, most probably at `your_laravel_project/storage/app/` 
// path (as per default Laravel storage config)
Storage::disk('local')->put('google.html', $contents);

// -- Here your have saved the file from the URL 
// -- to your local Laravel storage folder on your server.
// -- By default this is `your-laravel-project/storage/app` folder.

// Now, if desired, and if you are doing this within a web application's
// HTTP request (as opposite to CLI application)
// the file can be sent to the browser (client) with the response
// that instructs the browser to download the file at client side:

// Get the file path within you local filesystem
$path = Storage::url('google.html');

// Return HTTP response to a client that initiates the file downolad
return response()->download($path);

Look up Laravel Storage facade documentation for details on disk configuration and put method and Response with file download documentaion for returning file with an HTTP reponse.查找 Laravel Storage 外观文档以获取有关磁盘配置的详细信息,并使用文件下载文档put方法和响应以返回带有 HTTP 响应的文件。

为什么不只使用简单的重定向?

return \Redirect::to('https://my-cdn.com/files/image.jpg');

try this script:试试这个脚本:

// $main_url is path(url) to your remote file
$main_url = "http://dl.aviny.com/voice/marsieh/moharram/92/shab-02/mirdamad/mirdamad-m92-sh2-01.mp3";
header("Content-disposition:attachment; filename=$main_url");
readfile($main_url);

If you do not want end user can see main_url in header try this:如果您不希望最终用户可以在标头中看到 main_url,请尝试以下操作:

$main_url = "http://dl.aviny.com/voice/marsieh/moharram/92/shab-02/mirdamad/mirdamad-m92-sh2-01.mp3";
$file = basename($main_url);
header("Content-disposition:attachment; filename=$file");
readfile($main_url);

The one key thing here for me is being able to test the code.对我来说,这里的一件事是能够测试代码。 Using Laravel newer https://laravel.com/docs/8.x/http-client I can then use it like this使用较新的 Laravel https://laravel.com/docs/8.x/http-client我可以像这样使用它

Http::get($follower->profile_pic_url)->body();

Which looks like this to get the file and save看起来像这样获取文件并保存

$contents = Http::get($follower->profile_pic_url)->body();
Storage::disk("local")->put($path, $contents);

and then mock it in a test然后在测试中模拟它


Storage::shouldReceive("disk->exists")->once()->andReturn(false);
Storage::shouldReceive("disk->put")->once();
Http::shouldReceive("get->body")->once();

You can serve the file to download using a browser with the download() method您可以使用带有download()方法的浏览器提供要下载的文件

Documentation :文档

Example :示例

    return Storage::download('file.jpg');    
    return Storage::download('file.jpg', $name, $headers);
    // or
    return response()->download($pathToFile); 
    return response()->download($pathToFile, $name, $headers);

You could pull down the content of the original file, work out its mime type and then craft your own response giving the right headers.您可以提取原始文件的内容,计算出它的 mime 类型,然后制作您自己的响应并给出正确的标题。

I do this myself using a PDF library, but you could modify to use file_get_contents() to pull down the remote assets:我自己使用 PDF 库执行此操作,但您可以修改为使用file_get_contents()来下拉远程资产:

return Response::make(
        $pdf,
        200,
        array(
            'Content-Description' => 'File Transfer',
            'Cache-Control' => 'public, must-revalidate, max-age=0, no-transform',
            'Pragma' => 'public',
            'Expires' => 'Sat, 26 Jul 1997 05:00:00 GMT',
            'Last-Modified' => ''.gmdate('D, d M Y H:i:s').' GMT',
            'Content-Type' => 'application/pdf', false,
            'Content-Disposition' => ' attachment; filename="chart.pdf";',
            'Content-Transfer-Encoding' => ' binary',
            'Content-Length' => ' '.strlen($pdf),
            'Access-Control-Allow-Origin' => $origin,
            'Access-Control-Allow-Methods' =>'GET, PUT, POST, DELETE, HEAD, PATCH',
            'Access-Control-Allow-Headers' =>'accept, origin, content-type',
            'Access-Control-Allow-Credentials' => 'true')
        );

You need to change $pdf to be the data of the file, Content-Type to contain the mimetype of the file that the data is and Content-Disposition is the filename you want it to appear as.您需要将$pdf更改为文件的数据,将Content-Type更改为包含数据所在文件的 MIME 类型,并且Content-Disposition是您希望它显示为的文件名。

Not sure whether this will work, mine simply makes the browser popup with a PDF download so I'm not sure whether it'll work for embedding CDN files... Worth a try though.不确定这是否可行,我的只是让浏览器弹出一个 PDF 下载,所以我不确定它是否适用于嵌入 CDN 文件......不过值得一试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM