【问题标题】:PUT file with AWS presignedURL使用 AWS presignedURL 的 PUT 文件
【发布时间】:2014-03-17 00:46:46
【问题描述】:

我正在使用 aws-sdk-php 创建预签名 URL,用于将文件上传到 S3 存储桶。 GET 的 URL 有效。

这里是代码

$client = S3Client::factory(array('region' => 'eu-west-1','key' => 'xxx','secret' => 'xxx',));

//option 1
$command = $client->getCommand('PutObject', array(
    'Bucket' => 'myBucket',
    'Key' => 'testing/signedPHP1_'.time(),
    'ContentType' => 'image/jpeg',
    'Body' => 'dump' //it's mandatory
));
$signedUrl = $command->createPresignedUrl('+5 minutes');
$signedUrl .= '&Content-Type=image%2Fjpeg';
echo("\n\nThe URL is: ". $signedUrl . "\n");
echo("Now run from console for upload:\ncurl -v -H \"Content-Type: image/jpeg\" -T /tmp/temp.jpg '" . $signedUrl . "'");

//option 2
$request = $client->put('myBucket/testing/signedPHP2_'.time());
$signedUrl = $client->createPresignedUrl($request, '+5 minutes');
$signedUrl .= '&Content-Type=image%2Fjpeg';
echo("\n\nThe URL is: ". $signedUrl . "\n");
echo("Now run from console for upload:\ncurl -v -H \"Content-Type: image/jpeg\" -T /tmp/temp.jpg '" . $signedUrl . "'");

//GET which works
$request = $client->get('myBucket/testing/existingFile.txt');
$signedUrl = $client->createPresignedUrl($request, '+5 minutes');
echo("\n\nThe URL is: ". $signedUrl . "\n");
echo("Now run:\ncurl '" . $signedUrl . "'");

//GET which works
$command = $client->getCommand('GetObject', array('Bucket' => 'myBucket','Key' => 'upload/data.txt'));
$signedUrl = $command->createPresignedUrl('+5 minutes');
echo("\n\nThe URL is: ". $signedUrl . "\n");
echo("Now run:\ncurl '" . $signedUrl . "'");

尝试使用 curl 命令时出现错误 SignatureDoesNotMatch 带有消息 我们计算的请求签名与您提供的签名不匹配。检查您的密钥和签名方法。

aws-sdk 中用于 Javascript 的类似代码正在运行

var AWS = require('aws-sdk');
AWS.config.update({ accessKeyId: 'xxx', secretAccessKey: 'xxx', region: 'eu-west-1' });
var s3 = new AWS.S3();
var params = {
    Bucket: 'myBucket',
    Key: 'testing/preSignedURLnodeJS_' + (+new Date),
    ContentType: 'image/jpeg',
    Expires: 60 * 5
};
s3.getSignedUrl('putObject', params, function(err, url) {
    console.log('The URL is: ', url);
    console.log('Now run from console for upload:\n\ncurl -v -H "Content-Type: image/jpeg" -T /tmp/temp.jpg \'' + url + '\'');
});

已经做了很多研究但没有结果。我究竟做错了什么?

【问题讨论】:

    标签: php amazon-web-services amazon-s3


    【解决方案1】:

    在 Github 上,我得到了 SDK 开发人员的回答,上面写着

    $command = $client->getCommand('PutObject', array(
        'Bucket' => 'myBucket',
        'Key' => 'testing/signedPHP1_'.time(),
        'ContentType' => 'image/jpeg',
        'Body'        => '',
        'ContentMD5'  => false
    ));
    

    参考:https://github.com/aws/aws-sdk-php/issues/239

    【讨论】:

    • 我很困惑.. bennie j 似乎在下面给了你这个答案,但你提供了自己的答案而不是接受他的答案?看起来很糟糕……
    • 我知道你可以使用 putObject。但是如果需要允许第三方上传到您的私有存储桶,那么创建预签名 URL 是正确的方法。
    【解决方案2】:

    给你:

    首先,创建预签名 URL

    <?php 
    /**
     *  getDocumentUploadUrls
     *
     *  creates a list of url so you can upload multiple files per
     *  document. After uploading the files it is requires for you
     *  to confirm the uploads.
     *
     *  @param Int $documentId the document id
     *  @param Int $count the amount of files you want to upload for this document
     *
     *  @return Array list of URLs to use with PUT request to upload files to s3.
     */
    public function getDocumentUploadUrls(Int $documentId, Int $count = 1): Array
    {
        $awsService = $this->getService('aws');
    
        $s3Client = $awsService->getSdk()->createS3();
    
        $urls = array_fill(1, $count, null);
    
        $bucket = 'yourbucket';
    
        $result = [];
    
        $expire = '+20 minutes';
    
        for ($i = 0; $i < $count; $i++) {
            $fileCount = $i + 1;
    
            $key = "{$documentId}/{$fileCount}";
    
            $cmd = $s3Client->getCommand('PutObject', [
                'Bucket' => $bucket,
                'Key'    => $key
            ]);
    
            $request = $s3Client->createPresignedRequest($cmd, $expire);
    
            $result[] = [
                'url' => (string) $request->getUri(),
                'reference' => "{$bucket}/{$key}"
            ];
        }
    
        return $result;
    }
    

    结果可能类似于:

    $result = [
        0 => [
            'url' => 'AwsS3://put/uri',
            'reference' => 'docId/count'
        ]
    ];
    

    现在,用 php cURL 上传:

    if ($request->isPost()) {
            $files = $request->getFiles()->toArray();
    
            $files = reset($files);
    
            $data = $request->getPost();
    
            $docId = $data['documentId']; // get this from your frontend POST params
    
            $docCount = count($files);
    
            try {
                $endpoints = $this->getDocumentUploadUrls($userId, $docId, $docCount);
    
                $uploadInfo = $this->uploadDocuments($endpoints, $files);
            } catch (\Exception $e) {
                // handle exception
            }
    }
    
    public function uploadDocuments(Array $endpoints, Array $files)
    {
        $info = [];
    
        foreach ($files as $index => $file) {
            $url = $endpoints[$index]['url']; // the no. of files must match the number of endpoints for this to work
    
            $type = isset($file['type']) ? $file['type'] : 'application/octet-stream';
    
            $headers = [
                "Content-Type: $type",
                'Access-Control-Allow-Origin: *',
            ];
    
            $ch = curl_init($url);
    
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
            curl_setopt($ch, CURLOPT_POSTFIELDS, $file);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    
            $response = curl_exec($ch);
    
            if (curl_errno($ch)) {
                throw new \Exception(curl_error($ch));
            }
    
            curl_close($ch);
    
            $info[] = [
                'headers' => $headers,
                'response' => $response
            ];
        }
    
        return $info;
    }
    

    【讨论】:

    • 这就是解决方案。谢谢@Gus
    【解决方案3】:

    如果您使用的是 sdk,为什么不使用 putObject 命令,而不是自己创建一个 put 请求。这将为您处理signedUrl。

    $result = $client->putObject(array(
        'Bucket'     => $bucket,
        'Key'        => 'data_from_file.txt',
        'SourceFile' => $pathToFile
    ));
    

    http://docs.aws.amazon.com/aws-sdk-php/latest/class-Aws.S3.S3Client.html#_putObject

    如果您不想这样,您需要查看 put 命令的主体,这应该是您正在上传的文件的内容,而不仅仅是一个随机字符串 'dump'。

    在运行命令的机器上运行wireshark,你会看到curl发出的请求的主体是文件的内容。

    【讨论】:

    • 这不是问题!我想创建预签名 URL,将其发送给第三方,他是否可以在不知道访问/密钥的情况下将文件上传到我的存储桶。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-10
    • 1970-01-01
    • 1970-01-01
    • 2021-11-04
    • 1970-01-01
    • 1970-01-01
    • 2019-07-19
    相关资源
    最近更新 更多