简体   繁体   English

在Amazon S3上用PHP强制下载

[英]Force-Download with php on Amazon S3

I am trying to use http://code.google.com/p/amazon-s3-php-class/ to force-dowload files from AWS S3. 我正在尝试使用http://code.google.com/p/amazon-s3-php-class/从AWS S3强制下载文件。 I have an mp3 that I want people to "play" or "download." 我有一个MP3,希望人们“播放”或“下载”。 By default the when you access the file directly on s3 it begins to play in the browser. 默认情况下,当您直接在s3上访问文件时,它开始在浏览器中播放。 I need to add an option to actually download. 我需要添加一个选项才能实际下载。 I have Googled and found came up with nothing. 我用Google搜索,发现什么都没有。 I conceptually know what needs to happen but don't know how to produce it php. 我从概念上知道需要发生什么,但不知道如何生成php。 I know I need to modify the headers to Content-Disposition: attachment. 我知道我需要将标头修改为Content-Disposition:附件。 Any help would be greatly appreciated. 任何帮助将不胜感激。

Thanks, Michael 谢谢迈克尔

Amazon has now solved this problem and allows overriding of headers on a per-request basis with signed requests: 亚马逊现在已经解决了这个问题,并允许在每个请求的基础上使用已签名的请求覆盖标头:

http://docs.amazonwebservices.com/AmazonS3/latest/API/index.html?RESTObjectGET.html http://docs.amazonwebservices.com/AmazonS3/latest/API/index.html?RESTObjectGET.html

w00t! w00t!

The php scripts that have been mentioned so far will work ok, but the main downside is that every time a visitor on your site requests a file, your own servers will load it from the S3 and then relay that data to the browser. 到目前为止已经提到的php脚本可以正常工作,但是主要的缺点是,每次您网站上的访问者请求文件时,您自己的服务器都会从S3加载该文件,然后将该数据中继到浏览器。 For low traffic sites, it's probably not a big deal, but for high traffic ones, you definitely want to avoid running everything through your own servers. 对于低流量站点,这可能没什么大问题,但是对于高流量站点,您绝对要避免通过自己的服务器运行所有内容。

Luckily, there's a fairly straight-forward way to set your files to be forced to download from the S3. 幸运的是,有一种相当简单的方法可以将文件设置为强制从S3下载。 And you're exactly right - you just want to set the content-type and content-disposition (just setting content-disposition will work in some browsers, but setting both should work in all browsers). 而且您说得很对-您只想设置content-type和content-disposition(仅设置content-disposition将在某些浏览器中起作用,但是同时设置这两种设置在所有浏览器中都可以起作用)。

This code is assuming that you're using the Amazon S3 PHP class from Undesigned: 这段代码假设您正在使用Undesigned的Amazon S3 PHP类:

<?php

// set S3 auth and get your bucket listing

// then loop through the bucket and copy each file over itself, replacing the "request headers":
S3::copyObject($bucketName, $filename, $bucketName, $filename, "public-read", array(), array("Content-Type" => "application/octet-stream", "Content-Disposition" => "attachment"));

?>

Now all your files will be forced to download. 现在,您的所有文件将被强制下载。 You may need to clear your cache to see the change. 您可能需要清除缓存以查看更改。 And obviously, don't do that on any file that you actually do want to be loaded "inline" in the browser. 显然,不要在您实际上希望在浏览器中“内联”加载的任何文件上执行此操作。

The nice part with this solution is that applications that load media files directly (like let's say an mp3 player in Flash) don't care about the content-type or content-disposition, so you can still play your files in the browser and then link to download that same file. 此解决方案的优点在于,直接加载媒体文件的应用程序(例如Flash中的mp3播放器)无需关心内容类型或内容处置,因此您仍然可以在浏览器中播放文件,然后链接以下载相同的文件。 If the user already finished loading the file in flash, they'll most likely still have it in their cache, which means their download will be super quick and it won't even cost you any extra bandwidth charges from the S3. 如果用户已经完成了闪存文件的加载,他们很可能仍将其保存在缓存中,这意味着他们的下载将非常快捷,甚至不会从S3上花费您任何额外的带宽费用。

Just wanting to post a contribution to this, Alex Neth is correct on this reference, but i do not feel a link is sufficient information, using amazon's own AWS PHP SDK2. 只是想发表对此的贡献,Alex Neth在此参考上是正确的,但是我不觉得使用亚马逊自己的AWS PHP SDK2的链接是足够的信息。 Below I've outlined a basic (untested) method for calling data this way, you can use either S3 Factory Method or AWS Service Builder to make the S3 Client. 下面,我概述了一种以这种方式调用数据的基本(未调试)方法,您可以使用S3 Factory方法或AWS Service Builder来构建S3 Client。

<?php
// S3 Factory Method
/*use Aws\S3\S3Client;

$s3= S3Client::factory(array(
    'key'    => '<aws access key>',
    'secret' => '<aws secret key>'
));*/

// OR AWS Service Builder
use Aws\Common\Aws;

// Create a service builder using a configuration file
$aws = Aws::factory('/path/to/my_config.json');

// Get the client from the builder by namespace
$s3 = $aws->get('S3');

// Now lets create our request object.
$command = $s3->getCommand('GetObject',array(
    'Bucket'                        => 'your-bucket-name',
    'Key'                           => 'keyname',
    'ResponseContentType'           => 'application/octet-stream',
    'ResponseContentDisposition'    => 'attachment; filename="filename.mp3',
));
$url = $command->createPresignedUrl('+1 days');
?>

You can then use PHP's header("Location: $url"); 然后,您可以使用PHP的标头(“ Location:$ url”); in order to redirect the visitor to the MP3 file with a force download, this should prevent it from playing in the browser, Please note, i use ResponseContentType quite frequently but I've never used ResponseContentDisposition with AWS (it should work according to the docs). 为了通过强制下载将访问者重定向到MP3文件,这应防止它在浏览器中播放,请注意,我非常频繁地使用ResponseContentType,但我从未在AWS上使用过ResponseContentDisposition(它应根据文档工作)。

Converting this sample into a function should be easy, you could even pass in $bucket, $key, $force_download as such 将此示例转换为函数应该很容易,您甚至可以像这样传递$ bucket,$ key,$ force_download

<?php
use Aws\Common\Aws;

function gen_url($bucket,$key,$force_download=false){
    // OR AWS Service Builder (personal method to do this)

    // Create a service builder using a configuration file
    $aws = Aws::factory('/path/to/my_config.json');

    // Get the client from the builder by namespace
    $s3 = $aws->get('S3');
    $params = array(
        'Bucket'                        => $bucket,
        'Key'                           => 'keyname',
        'ResponseContentType'           => 'application/octet-stream',
        'ResponseContentDisposition'    => 'attachment; filename="filename.mp3',
    );

    if($force_download){
        $params['ResponseContentType'] = 'application/octet-stream';
        $params['ResponseContentDisposition'] = 'attachment; filename="'.basename($key).'"';
    }

    $command = $s3->getCommand('GetObject',$params);
    return $command->createPresignedUrl('+1 days');
}

// Location redirection to an MP3 force downlaod
header("Location: ".gen_url("recordings","my-file.mp3",true));
// Location redirection to a MP3 that lets the browser decide what to do.
header("Location: ".gen_url("recordings","my-file.mp3"));

?>

WARNING, if you haven't figured it out, this requires the AWS PHP SDK 2 currently (April 7th 2014) found here http://aws.amazon.com/sdkforphp/ This code is mostly pseudo code and may require some additional tweaking to actually make work as i'm referencing this from memory. 警告,如果您还没有弄清楚,这需要当前(2014年4月7日)在这里找到AWS PHP SDK 2 http://aws.amazon.com/sdkforphp/此代码主要是伪代码,可能需要进行一些其他调整实际使工作,因为我从内存中引用了这一点。

Just add this to your file's metadata on s3: 只需在s3上将其添加到文件的元数据中即可:

Content-Disposition: attachment; filename=FILENAME.EXT
Content-Type: application/octet-stream
<?php
    require_once __DIR__.'/vendor/autoload.php';
    use Aws\Common\Aws;

    function gen_url($bucket,$key,$force_download=false){
        // OR AWS Service Builder (personal method to do this)


        $config = array(
                'key'    => '',
                'secret' => '',
        );

        // Create a service builder using a configuration file
        $aws = Aws::factory($config);

        // Get the client from the builder by namespace
        $s3 = $aws->get('S3');
        $params = array(
            'Bucket'                        => $bucket,
            'Key'                           => $key,
            'ResponseContentType'           => 'application/octet-stream',
            'ResponseContentDisposition'    => 'attachment; filename="'.$key,
        );

        if($force_download){
            $params['ResponseContentType'] = 'application/octet-stream';
            $params['ResponseContentDisposition'] = 'attachment; filename="'.basename($key).'"';
        }

        $command = $s3->getCommand('GetObject',$params);
        return $command->createPresignedUrl('+1 days');
    }

    $bucket = '';
    $filename = '';


    $url = gen_url($bucket,$filename,true);

    echo "\n".$url."\n\n";

The code above works, you just need to install the S3 composer dependency and link in the autoload file, put your key/secret into config and then supply the bucket/filename. 上面的代码有效,您只需要安装S3 composer依赖项并在自动加载文件中链接,将您的密钥/秘密放入配置,然后提供存储桶/文件名即可。

Also worth mentioning is you are able to hard-set the headers for files in S3. 还值得一提的是,您可以在S3中硬设置文件头。 For example, if you need to force-download a certain file you can set the appropriate headers for that file. 例如,如果您需要强制下载某个文件,则可以为该文件设置适当的标题。 Such as defaulting to stream, and either having a secondary file set to force-download or spend the bandwidth to use php/fputs and force-download via PHP. 例如默认为流式传输,或者将辅助文件设置为强制下载或花费带宽使用php / fputs并通过PHP强制下载。

So modify my example above to be like this 所以修改我上面的例子是这样的


<?php

header('Content-Type: audio/mpeg');
header("Content-Disposition: attachment; filename={$_GET['file']};");

readfile("url to the file/{$_GET['file']}");

exit();

?>

Now you will want to put some validation in there so that you not giving the world access to every file you put on S3, but this should work. 现在,您将要在其中进行一些验证,以使您不会让世界访问您放在S3上的每个文件,但这应该可行。

If you are using a library like Tarzan AWS , you can add meta headers, that amazon will include when the file is retrieved. 如果您使用的是Tarzan AWS之类的库,则可以添加元标头,该标头将在检索文件时包含在Amazon中。 Check out the meta parameter in the update_object function here, for example: http://tarzan-aws.com/docs/2.0/files/s3-class-php.html#AmazonS3.update_object 在此处签出update_object函数中的meta参数,例如: http ://tarzan-aws.com/docs/2.0/files/s3-class-php.html#AmazonS3.update_object

This is now possible by overwriting the S3 headers using signed requests. 现在可以通过使用签名的请求覆盖S3标头来实现。

Request Parameters 请求参数

There are times when you want to override certain response header values in a GET response. 有时您想覆盖GET响应中的某些响应头值。 For example, you might override the Content-Disposition response header value in your GET request. 例如,您可以在GET请求中覆盖Content-Disposition响应标头值。

You can override values for a set of response headers using the query parameters listed in the following table. 您可以使用下表中列出的查询参数覆盖一组响应标题的值。

response-content-type - Sets the Content-Type header of the response response-content-disposition - Sets the Content-Disposition header of the response. response-content- type-设置响应的Content-Type标头response-content-disposition-设置响应的Content-Disposition标头。

Note 注意

You must sign the request, either using an Authorization header or a pre-signed URL, when using these parameters. 使用这些参数时,必须使用授权标头或预签名的URL对请求进行签名。 They cannot be used with an unsigned (anonymous) request. 它们不能与未签名(匿名)请求一起使用。

So, you would set those headers to: 因此,您可以将这些标头设置为:

response-content-disposition: attachment; filename=FILENAME.EXT
response-content-type: application/octet-stream

Found answer here: https://stackoverflow.com/a/5903710/922522 在这里找到答案: https : //stackoverflow.com/a/5903710/922522

php would just download the file to the server, not the client. php只会将文件下载到服务器,而不是客户端。 remember, php doesn't do anything on the client, it just returns content (html, javascript, css, xml, whatever...) 记住,php在客户端上不做任何事情,它只返回内容(html,javascript,css,xml等)。

[edit: added for clarity]: php can serve audio content, but you want to serve a web page and audio at the same time. [编辑:为清楚起见添加]:php可以提供音频内容,但是您想同时提供网页和音频。 To get that client behaviour, you have to get the client to request the file based on the web page's html or javascript. 要获得该客户端的行为,您必须使客户端根据网页的html或javascript请求文件。

So you have to get the client to download the file. 因此,您必须让客户端下载文件。 For instance, have an iframe on the page with the url of the file on s3. 例如,在页面上放置一个iframe,并在s3上添加文件的网址。 Use css to make the iframe invisible. 使用CSS使iframe不可见。 It should render the page and download and play the mp3. 它应该呈现页面并下载并播放mp3。

Otherwise, look into using javascript to kick of a download when the page loads. 否则,请在页面加载时考虑使用javascript进行下载。 I'm not sure if that's possible. 我不确定是否可行。

<?php
// PHP solution (for OP and others), works with public and private files
// Download url expires after 30 minutes (no experation after the download initiates, for large files)
// ***Forces client download***
$signed_url = $s3_client->getObjectUrl($s3_bucket_name, $s3_filename_key, '+30 minutes', array(
    'ResponseContentType' => 'application/octet-stream',
    'ResponseContentDisposition' => 'attachment; filename="your-file-name-here.mp4"'
));
redirect($signed_url);

I never tried Amazon's S3 hosting, but don't you have access to using .htaccess files there? 我从未尝试过Amazon的S3托管,但是您不可以在那里使用.htaccess文件吗? Then you can set Content-Type and Content-Disposition for an entire directory with this entry: 然后,您可以使用以下条目为整个目录设置Content-Type和Content-Disposition:

<IfModule mod_headers.c>
    <FilesMatch "\.(mp3)$">
            ForceType audio/mpeg
            Header set Content-Disposition attachment
    </FilesMatch>
</IfModule>

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

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