为什么 Google 存储 API 不能与 Amazon SDK v3 一起使用?

Why doesn't Google Storage API work with Amazon SDK v3?

我觉得很奇怪 post 在这里,但由于 SO 是 Google API 支持的唯一官方渠道,我想我需要在这里问一下。

If you have API, tool usage, or other software development-related questions, search for and post questions on Stack Overflow, using the official google-cloud-storage tag.

好的,开始吧。我花了两天的大部分时间努力让 Google 存储在 Amazon PHP SDK 的 v3 版本(最新)上工作。我不能使用旧版本的 SDK,因为我试图坚持使用 Laravel 5.1 的文件系统,而不必为 Google 存储编写全新的 driver。我相信这符合 Google 宣传 Google 存储的精神:

https://cloud.google.com/storage/docs/migrating

In a simple migration from Amazon S3 to Google Cloud Storage, you can use your existing tools and libraries for generating authenticated REST requests to Amazon S3, to also send authenticated requests to Google Cloud Storage. The changes you need to make to your existing tools and libraries are described in this section.

To get set up for a simple migration do the following:

Set a default Google project.

Get a developer key.

In your existing tools or libraries, make the following changes: Change the request endpoint to use the Google Cloud Storage request endpoint. Replace the Amazon Web Services (AWS) access and secret key with the corresponding Google Cloud Storage access key and secret key (collectively called your Google developer key).

That's it! At this point you can start using your existing tools and libraries to send keyed-hash message authentication code (HMAC) requests to Google Cloud Storage.

好棒的球场!让我们尝试使用使用 gsutil 工作的互操作性凭据。

$client = new S3Client([
    'credentials' => [
        'key' => 'GOOGxxxxxxxxxxxxxxx',
        'secret' => 'ZfcOTxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    ],
    'region' => 'US',
    'version' => 'latest',
    'endpoint' => 'https://storage.googleapis.com',
]);

try {
    $result = $client->putObject(array(
        'Bucket' => 'devtest',
        'Key' => 'test',
        'Body' => 'Hello world'
    ));

    echo $result['ObjectURL'];
} catch (\Aws\S3\Exception\S3Exception $e) {
    // The AWS error code (e.g., )
    echo $e->getAwsErrorCode() . "\n";
    // The bucket couldn't be created
    echo $e->getMessage() . "\n";
}

无效。你得到一个 "Incorrect Authentication Header"。让我们来看看那个header.

AWS4-HMAC-SHA256 Credential=GOOGGUxxxxxxxxxxx/20150611/US/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=9c7de4xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

此时我为此创建了一个 SO post,有人建议我添加 'signature' => 'v2'.

让我们试试看:

$client = new S3Client([
    'credentials' => [
        'key' => 'GOOGxxxxxxxxxxxxxxx',
        'secret' => 'ZfcOTxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    ],
    'region' => 'US',
    'version' => 'latest',
    'endpoint' => 'https://storage.googleapis.com',
    'signature' => 'v2',
]);

运气不好。同样的错误。授权 header 没有改变。我们看一下S3Client的代码,看看'signature'是如何使用的:

public function createPresignedRequest(CommandInterface $command, $expires)
{
    /** @var \Aws\Signature\SignatureInterface $signer */
    $signer = call_user_func(
        $this->getSignatureProvider(),
        $this->getConfig('signature_version'),
        $this->getApi()->getSigningName(),
        $this->getRegion()
    );

    return $signer->presign(
        \Aws\serialize($command),
        $this->getCredentials()->wait(),
        $expires
    );
}

没有。所以现在我们偏离了 S3 的官方文档,因为他们说了同样的话:

http://docs.aws.amazon.com/aws-sdk-php/guide/latest/configuration.html

不是 'signature',是 'signature_version'。让我们将其更改为 v2.

$client = new S3Client([
    'credentials' => [
        'key' => 'GOOGxxxxxxxxxxxxxxx',
        'secret' => 'ZfcOTxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    ],
    'region' => 'US',
    'version' => 'latest',
    'endpoint' => 'https://storage.googleapis.com',
    'signature_version' => 'v2',
]);

至少这次我们得到了不同的错误!

UnresolvedSignatureException in SignatureProvider.php line 61: Unable to resolve a signature for v2/s3/US. Valid signature versions include v4 and anonymous.

所以,在玩弄了两天之后,看起来这是不可能的,至少不像 Google 希望你相信他们的推销那么容易。我根本无法让它工作,所以我希望这里有人可以对此有所了解。要么我错过了一些重要的东西,要么 Google 虚假宣传 Google 存储使用亚马逊的 S3 SDK 工作并浪费开发人员的时间。我在想也许我们必须手动劫持授权 header,但这超出了我的专业知识范围。任何帮助将不胜感激。

所以我要 post 比从不晚,并说我正在使用 laravel 5.1,你必须选择是否要使用 AWS 或 GCS配置时间,因为您无法安装两个版本的 aws php 客户端(这将您对 flysystem 的选择限制为 v2 或 v3。GCS 暂时需要 v2,而 laravel 中的 s3 实现需要v3。我花了 小时 试图让 v3 与 GCS 一起工作,但身份验证 headers 不同,所以我没有费心。

您必须提供自己的提供商,但设置起来并不难。只需在 app/Providers

中创建 GcsAppsServiceProvider.php
<?php namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Storage;
use Aws\S3\S3Client;
use League\Flysystem\AwsS3v2\AwsS3Adapter;
use League\Flysystem\Filesystem;

class GcsAppsServiceProvider extends ServiceProvider
{
    public function register()
    {

    }

    public function boot()
    {
        Storage::extend('gcs', function( $app, $config )
        {
            $client = S3Client::factory([
                'key'    => $config['key'],
                'secret' => $config['secret'],
                'base_url' => $config['base_url'],
            ]);

            return new Filesystem(new AwsS3Adapter($client, $config['bucket']));
        });
    }
}

基本上在您的配置中,您只是在 filesystem.php 中复制 s3 配置并更改区域 -> base_url。您还可以自由地将默认值和云更改为 gcs 以支持 google 云存储。

's3' => [
    'driver' => 's3',
    'key'    => env('AWS_KEY', 'your-key'),
    'secret' => env('AWS_SECRET', 'your-secret'),
    'region' => env('AWS_REGION','your-region'),
    'bucket' => env('AWS_BUCKET','your-bucket'),
],

'gcs' => [
    'driver' => 'gcs',
    'key'    => env('GCS_KEY', 'your-key'),
    'secret' => env('GCS_SECRET', 'your-secret'),
    'base_url' => env('GCS_BASE_URL','your-base-url'),
    'bucket' => env('GCS_BUCKET','your-bucket'),
],

最后但同样重要的是,您还需要将提供商添加到 app.php

中的提供商列表中
'providers' => [
    ...
    App\Providers\GcsAppsServiceProvider::class,
],

AWS S3 适配器还提到 GCS supports it in their documentation