(最後更新: 2016-04-27)
介紹
以下會介紹上傳到 s3 的 2 種方法 :
- mobile 上傳到 s3
- server 上傳到 s3
command 上傳到 s3, 請參考此篇 AWS-CLI command 上傳到 s3
安裝 AWS SDK
首先先用 composer 安裝 aws-sdk, composer.json :
{
"require": {
"aws/aws-sdk-php": "2.7.0"
}
}
執行
$ composer install
Codeigniter (application/config/config.php) 打開 : $config['composer_autoload'] = TRUE;
mobile 上傳到 s3
有兩種方法
- 直接給手機一個 S3 的 IAM User 讓手機以 SDK 上傳圖片
- S3 server 產生有 expire time 的 pre-signed URL (由 server 取得授權的 url) 給 mobile 上傳圖片
直接給手機一個 S3 的 IAM User 讓手機以 SDK 上傳圖片
原本的 Resource 的值是 *
, 將它改成以下
Policy for Programmatic Access : (線上 AWS Console 是沒權限看到的)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::test"]
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectACL",
"s3:GetObject",
"s3:GetObjectACL",
"s3:DeleteObject"
],
"Resource": ["arn:aws:s3:::test/*"]
}
]
}
但是設定好以上, 雖然程式可以有權限存取, 但是 AWS Console 是沒有權限訪問 S3 的, 所以可以參考以下讓這個 User 能看到 S3 的 Console
Policy for Console Access : (線上 AWS Console 是可以看到目錄結構的)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation",
"s3:ListAllMyBuckets"
],
"Resource": "arn:aws:s3:::*"
},
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::test"]
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectACL",
"s3:GetObject",
"s3:GetObjectACL",
"s3:DeleteObject"
],
"Resource": ["arn:aws:s3:::test/*"]
}
]
}
這樣 mobile 就只能存取 test-mobile 這個 bucket 下的資源了
注意!! 上傳的檔案到 server 的權限是沒有 public-read 的, 所以多加兩個權限 PutObjectACL
(更新權限), GetObjectACL
(查看權限)
S3 server 產生有 expire time 的 pre-signed URL (由 server 取得授權的 url) 給 mobile 上傳圖片
create pre-signed url :
public function create_pre_signed_url()
{
$config = array(
'key' => 'A******************Q',
'secret' => 'P**************************************4',
'region' => 'us-west-2',
);
$client = S3Client::factory($config);
$bucket = "s3_bucket_name";
$key = 'data.txt';
$url = "{$bucket}/{$key}";
// get() returns a Guzzle\Http\Message\Request object
$request = $client->put($url);
// Create a signed URL from a completely custom HTTP request that
// will last for 10 minutes from the current time
$signedUrl = $client->createPresignedUrl($request, '+10 minutes');
echo $signedUrl;
//echo file_get_contents($signedUrl);
// > Hello!
}
- 因為要讓這個 pre-signed url 可以被上傳檔案, 所以使用
$client->put(
- 如果改成
$client->get(
輸出的 URL 就可以看到 bucket 的 data.txt 的內容, 那這功能就跟 getObjectUrl
一樣了
要測試它是否 OK 要實際用上傳檔案,不要用瀏覽器開,不然只會得到 XML 錯誤訊息
<Error>
<Code>SignatureDoesNotMatch</Code>
</Error>
使用 command 模擬 mobile 上傳。建立 /tmp/data.txt
, 然候執行以下指令上傳檔案
curl -v -T /tmp/data.txt https://s3_bucket_name.s3.amazonaws.com/data.txt?AWSAccessKeyId=A********************2Q&Expires=1423551936&Signature=y**************2FvYVo%3D
- 不管傳什麼格式,不用特別指定 Header
-T
是使用 PUT 上傳檔案
ref :
server 上傳至 s3
到 IAM 建立一個 User, policy 新增只允許存取某個 bucket 下某個 folder 的 custom policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation",
"s3:ListAllMyBuckets"
],
"Resource": "arn:aws:s3:::*"
},
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::bucket_name"]
},
{
"Effect": "Allow",
"Action": "*",
"Resource": ["arn:aws:s3:::bucket_name/folder_name/*"]
}
]
}
Uploading files to s3 in php
$config = array(
'key' => 'A******************A',
'secret' => '9**************************************i',
'region' => 'us-west-2',
);
$client = S3Client::factory($config);
$result = $client->putObject([
'Bucket' => 'bucket_name',
'Key' => 'folder_name/qqq/test.txt',
'SourceFile' => '/tmp/qq.txt',
]);
不存在的 folder 會自動建立, 如果檔案已存在會覆蓋原始檔案
刪除 s3 檔案
TODO: 補上 code
這裡有個很容易踩雷的點,而且錯誤訊息不好判斷,就是要刪除的檔案路徑最前面不要加 /
,直接路徑加檔名即可 e.g. xxxx/ffff/ssss.mp4
[其他] 產生 S3 只能 GET 不能 PUT 的 Pre-signed URL
get pre-signed url :
use Aws\S3\S3Client;
class S3 extends MY_Controller
{
public function __construct()
{
parent::__construct();
}
public function get_pre_signed_url()
{
$config = array(
'key' => 'A******************Q',
'secret' => 'P**************************************4',
);
$client = S3Client::factory($config);
// Get a plain URL for an Amazon S3 object
$plainUrl = $client->getObjectUrl('s3_bucket_name', 'data.txt');
// > https://my-bucket.s3.amazonaws.com/data.txt
// Get a pre-signed URL for an Amazon S3 object
$signedUrl = $client->getObjectUrl('s3_bucket_name', 'data.txt', '+10 minutes');
// > https://my-bucket.s3.amazonaws.com/data.txt?AWSAccessKeyId=[...]&Expires=[...]&Signature=[...]
// Create a vanilla Guzzle HTTP client for accessing the URLs
$http = new \Guzzle\Http\Client;
// Try to get the plain URL. This should result in a 403 since the object is private
try {
$response = $http->get($plainUrl)->send();
} catch (\Guzzle\Http\Exception\ClientErrorResponseException $e) {
$response = $e->getResponse();
}
echo $response->getStatusCode();
// > 403
// Get the contents of the object using the pre-signed URL
echo $signedUrl;
$response = $http->get($signedUrl)->send();
echo $response->getBody();
// > Hello!
}
}
?>
以上面程式產生 pre-signed url, 然候頁面上會印出 url, 再拿這個 url 就可以看到檔案裡的內容了
ref: