本文转自残阳似血的博客
首先还是要先说一下Amazon S3,全称是Amazon Simple Storage Service。EC2和S3是Amazon最早推出的两项云服务。在传统的计算机领域,主要包括计算、存储、网络这几个方面, 在云计算时代,前两者分别对应虚拟化和cloud storage,由此可以显现出Amazon EC2和S3的重要性。如今随着云计算的大红大紫,也有很多使用Amazon S3的例子,典型的有Dropbox,还有之前被FB收购的Instagram,其照片存储就使用的S3。
关于REST,这也是比较火的一种Web服务架构。简单来说,资源是由URI指定,对资源的操作包括GET、PUT、POST、DELETE和HEAD,返回结果常常是XML或者其他形式。如果你想了解更多,可以查看REST的Wiki页面。
Amazon S3的操作包括三部分:Service,Buckets和Objects。Service只包括GET操作,就是返回所有的Buckets的列表。Object顾名思义,是指存储在云端的文件,值得注意的是,S3中并没有明确的文件夹的概念,而是通过指定object的路径来实现,比如说,object可以为“photos/1.jpg”。而Bucket拥有全局名,名称由用户定义,用来存放Object,由于是全局名,所以要确保名字是别人没用过的。
访问Web服务时,Http request headers需要一些参数。主要包括:
- Date:当前UTC时间,形式为“ Wed, 01 Mar 2009 12:00:00 GMT ”。
- Content-Length: 当对Object进行操作的时候,返回内容的长度,注意不要包括headers中的内容。
- Content-MD5:用base64编码文件内容的MD5值。
- Content-Type:资源的类型,比如:text/plain。
- Host:Get Service时为“ s3.amazonaws.com ”。在对bucket和object进行操作时,例如bucket的名字是“bucketname”,那么Host就是“bucketname.s3.amazonaws.com”。
- x-amz-meta-和x-am-开头的:包括Amazon定义的一些元数据和一些特定的header。后面如果出现会提到。
- Authorization:这个是最重要的,主要作用是签名,Amazon根据的请求计算出一个签名值和这里计算的签名值进行比对,只有相同时,访问才是合法的。接下来对Authorization的计算方法进行详述。
Authorization的计算方法
这个是Amazon文档中的说明:
Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;
Signature = Base64( HMAC-SHA1( YourSecretAccessKeyID, UTF-8-Encoding-Of( StringToSign ) ) );
StringToSign = HTTP-Verb + "\\n" +
Content-MD5 + "\\n" +
Content-Type + "\\n" +
Date + "\\n" +
CanonicalizedAmzHeaders +
CanonicalizedResource;
CanonicalizedResource = \[ "/" + Bucket \] +
<HTTP-Request-URI, from the protocol name up to the query string\> +
\[ sub-resource, if present. For example "?acl", "?location", "?logging", or "?torrent"\];
CanonicalizedAmzHeaders = <described below>
``````
首先Authorization是由”AWS {0}:{1}“组成的,第0个参数为你的Access Key ID,在注册了AWS之后,在[这里](https://aws-portal.amazon.com/gp/aws/securityCredentials)可以得到。第1个参数是计算出来的签名值。
签名值的计算方法是对一个UTF-8的字符串,用你的Secret Access Key(同样在Access Key ID处获取)进行SHA1加密。
StringToSign字符串也需要满足一定的格式。如上所示,第一行是你的操作名,应该为PUT、GET、DELETE、HEAD和POST中的一种。第二行是内容的MD5值的base64编码,和headers中的Content-MD5值应保持一致。第三行是Content-Type,同样需要和headers中的一致。第四行Date,和headers中的Date一致。需要说明的是CanonicalizedAmzHeaders和CanonicalizedResource。
CanonicalizedAmzHeaders就是把headers中的x-amz-开头的作为key转化为小写并按顺序排列,key和value之间用冒号相连,用换行符“\\n”把它们给连接起来。比如说headers中有:
> X-Amz-Meta-ReviewedBy: [email protected] X-Amz-Meta-ReviewedBy: [email protected] X-Amz-Meta-FileChecksum: 0x02661779 X-Amz-Meta-ChecksumAlgorithm: crc32 > ```
那么CanonicalizedAmzHeaders就是:
> ```
> x-amz-meta-checksumalgorithm:crc32\\n x-amz-meta-filechecksum:0x02661779\\n x-amz-meta-reviewedby:[email protected],[email protected]
> ```
CanonicalizedResource是指规范化的资源。如果访问资源没有指定bucket,那么就是“/”;如果包括bucket,而不包括object,那就是“/bucket_name/”,注意前后的“/”不要落了;如果既包括bucket,也包括object,那么就是“/bucket_name/object_name”;另外,有时候比如是访问bucket的acl(访问控制列表acess control list)时,object_name就是?acl,因此这时CanonicalizedResource就是“/bucket_name/?acl”,访问object的acl时,CanonicalizedResource 就是“/bucket_name/object_name?acl”。
需要说明的是,如果计算出的CanonicalizedAmzHeaders不为空时,要确保CanonicalizedAmzHeaders和CanonicalizedResource之间有换行符“\n”连接。
关于Authorization的详细内容,可以参考官方文档。
##### Service
对Service的操作只包括Get,即获取用户所有的Buckets列表。Request headers除了通用的,没有其他的内容。比如
> ```
> GET / HTTP/1.1 Host: s3.amazonaws.com Date: date Authorization: signatureValue
> ```
返回的XML中包括Owner和各个Buckets,比如:
> ```
> <?xml version="1.0" encoding="UTF-8"?>
> ```
>
> ```
> <ListAllMyBucketsResult xmlns\="http://doc.s3.amazonaws.com/2006-03-01"\>
> ```
>
> ```
> <Owner\>
> ```
>
> ```
> <ID\>bcaf1ffd86f461ca5fb16fd081034f</ID\>
> ```
>
> ```
> <DisplayName\>webfile</DisplayName\>
> ```
>
> ```
> </Owner\>
> ```
>
> ```
> <Buckets\>
> ```
>
> ```
> <Bucket\>
> ```
>
> ```
> <Name\>quotes</Name\>
> ```
>
> ```
> <CreationDate\>2006-02-03T16:45:09.000Z</CreationDate\>
> ```
>
> ```
> </Bucket\>
> ```
>
> ```
> <Bucket\>
> ```
>
> ```
> <Name\>samples</Name\>
> ```
>
> ```
> <CreationDate\>2006-02-03T16:41:58.000Z</CreationDate\>
> ```
>
> ```
> </Bucket\>
> ```
>
> ```
> </Buckets\>
> ```
>
> ```
> </ListAllMyBucketsResult\>
> ```
##### Buckets
由于项目中只用到了Buckets的PUT、GET、DELETE,关于acl、lifecycle、policy等就不作过多说明,如果这方面有疑问,可以参考官方文档。
接下来,如果Http request headers中内容没有什么特别说明的,将会略去不写。
###### [PUT Bucket](http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUT.html)
需要说明的是,在request headers可以加入bucket的权限控制,即指定x-amz-acl,合法的值包括:private,public-read、public-read-write、authenticated-read、bucket-owner-read、bucket-owner-full-control,从名字就可以看出具体的含义。
在request body中可以包括位置信息,即用户期望Bucket放置在Amazon的哪个数据中心。默认为US Standard,其他数据中心包括US West (Oregon) Region、US West (Northern California) Region、EU (Ireland) Region、Asia Pacific (Singapore) Region、Asia Pacific (Tokyo) Region、South America (Sao Paulo) Region。对于我们中国用户来说,离得最近的是东京的数据中心。不过在body中内容中,这七个数据中心写成:’EU’、 ‘eu-west-1′、’us-west-1′、 ‘us-west-2′、’ap-southeast-1′、’ap-northeast-1′和’sa-east-1′。
比如请求如下:
> ```
> PUT / HTTP/1.1 Host: BucketName.s3.amazonaws.com Content-Length: length Date: date Authorization: signatureValue
> ``````
> <CreateBucketConfiguration xmlns\="http://s3.amazonaws.com/doc/2006-03-01/"\>
> ```
>
> ```
> <LocationConstraint\>BucketRegion</LocationConstraint\>
> ```
>
> ```
> </CreateBucketConfiguration\>
> ```
###### [GET Bucket](http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGET.html)
Get Bucket主要是列出这个Bucket下所有的objects。值得提的是四个参数Prefix、Marker、MaxKeys和Delimiter,利用这四个参数,可以达到多种效果。
首先是Prefix,它表示这个Bucket中返回的Object以这个值为开头。Marker表示,返回这个值以后的Objects,比如说,第一次调用没有返回全部结果,则把第一次调用返回的Objects的最后一个作为Maker调用,以返回其以后的Objects。MaxKeys返回单次请求返回的最大Objects数,默认为1000。Delimiter表示分隔符,是在设置了Prefix之后,能够返回共同的Prefix(在结果中为CommonPrefix)。
因此,通过设置MaxKeys和Marker可以达到翻页效果,每次返回的最后一个Object作为下一次请求的Marker,在返回值中,如果IsTruncated为true,那么表示还有下一页。此外,通过设置Prefix和将Delimiter设为”/“,可以达到返回某个文件夹下所有内容的效果,其中CommonPrefix下的Prefix表示文件夹路径,而每个Contents中是Object的信息。
下面是一个请求的例子:
> ```
> GET ?prefix=N&marker=Ned&max-keys=40 HTTP/1.1 Host: quotes.s3.amazonaws.com Date: Wed, 01 Mar 2009 12:00:00 GMT Authorization: AWS AKIAIOSFODNN7EXAMPLE:xQE0diMbLRepdf3YB+FIEXAMPLE=
> ```
返回结果为:
> ```
> HTTP/1.1 200 OK x-amz-id-2: gyB+3jRPnrkN98ZajxHXr3u7EFM67bNgSAxexeEHndCX/7GRnfTXxReKUQF28IfP x-amz-request-id: 3B3C7C725673C630 Date: Wed, 01 Mar 2009 12:00:00 GMT Content-Type: application/xml Content-Length: 302 Connection: close Server: AmazonS3
> ``````
> <?xml version="1.0" encoding="UTF-8"?>
> ```
>
> ```
> <ListBucketResult xmlns\="http://s3.amazonaws.com/doc/2006-03-01/"\>
> ```
>
> ```
> <Name\>bucket</Name\>
> ```
>
> ```
> <Prefix/>
> ```
>
> ```
> <Marker/>
> ```
>
> ```
> <MaxKeys\>1000</MaxKeys\>
> ```
>
> ```
> <IsTruncated\>false</IsTruncated\>
> ```
>
> ```
> <Contents\>
> ```
>
> ```
> <Key\>my-image.jpg</Key\>
> ```
>
> ```
> <LastModified\>2009-10-12T17:50:30.000Z</LastModified\>
> ```
>
> ```
> <ETag\>"fba9dede5f27731c9771645a39863328"</ETag\>
> ```
>
> ```
> <Size\>434234</Size\>
> ```
>
> ```
> <StorageClass\>STANDARD</StorageClass\>
> ```
>
> ```
> <Owner\>
> ```
>
> ```
> <ID\>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID\>
> ```
>
> ```
> <DisplayName\>[email protected]</DisplayName\>
> ```
>
> ```
> </Owner\>
> ```
>
> ```
> </Contents\>
> ```
>
> ```
> <Contents\>
> ```
>
> ```
> <Key\>my-third-image.jpg</Key\>
> ```
>
> ```
> <LastModified\>2009-10-12T17:50:30.000Z</LastModified\>
> ```
>
> ```
> <ETag\>"1b2cf535f27731c974343645a3985328"</ETag\>
> ```
>
> ```
> <Size\>64994</Size\>
> ```
>
> ```
> <StorageClass\>STANDARD</StorageClass\>
> ```
>
> ```
> <Owner\>
> ```
>
> ```
> <ID\>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID\>
> ```
>
> ```
> <DisplayName\>[email protected]</DisplayName\>
> ```
>
> ```
> ...
> ```
###### [DELETE Bucket](http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketDELETE.html)
DELETE Bucket没有什么需要特殊说明的。
##### Objects
###### [PUT Object](http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectPUT.html)
PUT Object基本操作类似,在Http body中添加Object的内容,这里就需要计算Content-Type等值。与PUT Bucket类似,可以在Http headers中加入x-amz-acl,以控制Object的权限。
###### [GET Object](http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGET.html)
在GET Object时,Response headers中会包括这个Object的相关信息,除了Content-Length和Content-Type等,Etag其实就是内容的MD5后的16进制的字符串。而Response body中就是文件的内容。
###### [DELETE Object](http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectDELETE.html)
DELETE操作仍然没有什么特别说明的。
综上,本文主要说明了Amazon S3中对Service的GET,以及对Bucket和Object的PUT、GET和DELETE操作。但是REST API中还有一部分没有提到,这个可以查看具体的文档,其中解释得还是比较详细的。这篇文章还是为了想写Amazon S3客户端调用的同学们提供导向作用。
最后,提及一下当时的项目,项目主要是要实现一个本地的多云存储的备份。我们的项目使用了Python,而Amazon S3有一些第三方的API调用的实现,比较知名的有Boto,但是由于其支持很多云服务,显得过于庞大,最后我决定自己实现,而Amazon S3这块的中文资料也不是很多,这里分享出来,让入门的同学能够更快的上手,仅此而已。如果想看我们项目中Amazon S3 REST API的客户端实现,可以移步这里。
**原创文章,转载请注明:** 转载自[Happiness space](https://byszsz.com/)
**本文链接地址:** [Amazon S3 REST API详解(转载)](https://byszsz.com/archives/554.htm)