使用PHP在Amazon S3上进行强制下载

14
我正在尝试使用http://code.google.com/p/amazon-s3-php-class/来从AWS S3强制下载文件。我有一个mp3文件,希望人们可以“播放”或“下载”。默认情况下,当您直接在s3上访问该文件时,它会在浏览器中开始播放。我需要添加一个选项来实际下载文件。我已经尝试过谷歌搜索但没有找到合适的结果。我大致知道需要发生什么但不知道如何用php实现。我知道我需要修改标题为Content-Disposition: attachment的头文件。非常感谢任何帮助。

谢谢, Michael


https://dev59.com/-XE85IYBdhLWcg3wyGl_ - andrewrk
12个回答

21

10

到目前为止提到的PHP脚本可以正常工作,但主要缺点是每当您网站上的访问者请求文件时,您自己的服务器将从S3加载该文件,然后将数据传输到浏览器。对于低流量站点,可能并不重要,但对于高流量站点,您肯定要避免通过自己的服务器运行所有内容。

幸运的是,有一种相当直接的方法可以设置您的文件强制从S3下载。并且您的想法很正确- 您只需要设置内容类型和内容规定(在某些浏览器中仅设置内容规定就可以工作,但在所有浏览器中设置两者都应该可以)。

此代码假设您正在使用来自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"));

?>

现在所有的文件都将被强制下载。您可能需要清除缓存才能看到更改。当然,不要对任何实际上需要在浏览器中"内联"加载的文件执行此操作。

该解决方案的好处是,直接加载媒体文件的应用程序(比如Flash中的MP3播放器)不关心内容类型或内容分发,因此您仍然可以在浏览器中播放文件,然后链接到下载同一文件。如果用户已经在Flash中完成了文件的加载,他们很可能仍然将其保存在缓存中,这意味着他们的下载将非常快,并且它甚至不会给您带来任何额外的S3带宽费用。


1
这是一个相当大的缺点。我认为这个问题还没有得到解答。 - andrewrk
1
亚马逊发布了一个解决这个问题的功能。 - Alex Neth

4

只需将此添加到您在S3上的文件元数据中:

Content-Disposition: attachment; filename=FILENAME.EXT
Content-Type: application/octet-stream

3

我想为这个贡献发表一些意见。Alex Neth在这方面是正确的,但我认为提供一个链接是不足够的信息。可以使用亚马逊自己的AWS PHP SDK2来调用数据。下面是一种基本的(未经测试的)方法,可以使用S3 Factory Method或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');
?>

你可以使用PHP的header("Location: $url");将访问者重定向到MP3文件,并强制下载,这应该可以防止在浏览器中播放,请注意,我经常使用ResponseContentType但从未在AWS中使用过ResponseContentDisposition(根据文档应该是有效的)。
将此示例转换为函数应该很容易,你甚至可以传入$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"));

?>

警告,如果你还没有想到,这需要目前(2014年4月7日)找到的AWS PHP SDK 2,网址在http://aws.amazon.com/sdkforphp/。这段代码大部分是伪代码,可能需要一些额外的调整才能实际工作,因为我是根据记忆引用的。


这个不起作用。我尝试了以下代码:header('Content-disposition: attachment; filename=s3112.png'); header('Content-type: image/png'); header("Location: https://s3-ap-southeast-1.amazonaws.com/mytest.sample/s3112.png"); - urfusion

1

所以将我的上面的例子修改成这样


<?php

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

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

exit();

?>

现在你需要加入一些验证,以免将每个文件都放在S3上并让全世界都可以访问,但这应该可以工作。

1
<?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";

上面的代码可以正常工作,你只需要安装S3组件依赖并将其链接到自动加载文件中,将您的密钥/秘密放入配置文件中,然后提供存储桶/文件名即可。

1
值得一提的是,您可以在S3中硬编码文件头。例如,如果您需要强制下载某个文件,则可以为该文件设置适当的标头。例如,默认流式传输,并将第二个文件设置为强制下载或使用php / fputs和通过PHP强制下载来消耗带宽。

1

0

通过使用签名请求覆盖S3头部现在是可行的。

请求参数

有时您想要在GET响应中覆盖某些响应头值。例如,您可能会在GET请求中覆盖Content-Disposition响应头值。

您可以使用以下表格中列出的查询参数覆盖一组响应头的值。

response-content-type - 设置响应的Content-Type头 response-content-disposition - 设置响应的Content-Disposition头。

注意

当使用这些参数时,您必须使用授权标头或预签名URL对请求进行签名。它们不能用于未签名(匿名)请求。

因此,您将设置这些头部:

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

在这里找到答案:https://dev59.com/63NA5IYBdhLWcg3wrf03#5903710


0

PHP 只会将文件下载到服务器,而不是客户端。请记住,PHP 在客户端上不执行任何操作,它只返回内容(HTML、JavaScript、CSS、XML 等等)。

[编辑:为了更清晰]: PHP 可以提供音频内容,但您想同时提供网页和音频。要获得该客户端行为,您必须让客户端基于网页的 HTML 或 JavaScript 请求文件。

因此,您必须让客户端下载文件。例如,在页面上放置一个具有 S3 文件 URL 的 iframe。使用 CSS 使 iframe 不可见。它应该呈现页面并下载和播放 MP3。

否则,请查看使用 JavaScript 在页面加载时启动下载。我不确定是否可能。


是的,你说得对。我想要的最终结果是有一个文件,即s3download.php,我可以将需要下载的文件传递给它。例如,下载将如下所示: <a href="s3download.php?file=test.mp3">下载</a> 当调用该文件时,它会访问S3帐户并添加内容分发标头,从而导致文件开始下载。 这有意义吗? - michaelespinosa
对于s3download.php,您的步骤可能如下:-设置内容类型的标头值为mp3数据 -使用curl、http请求或您喜欢的任何方法从s3下载,写入临时文件 -读取文件内容并返回给用户我无法在评论中放置所有示例,但如果您在谷歌上搜索每个步骤的示例,您应该会找到所需的内容。 - Tim Hoolihan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接