你可以使用 REST API 直接从 iPhone 上传到 S3,并让服务器负责生成需要密钥的 Authorization 标头值的部分。这样,您就不会冒着将访问密钥暴露给越狱 iPhone 用户的风险,同时也不会将文件上传的负担放在服务器上。有关要进行的请求的详细信息,请参见“签署和认证 REST 请求”的“示例对象 PUT”
"Example Object PUT"。我
强烈建议在继续之前阅读该文档。
以下 Python 代码生成从您的 S3 秘密访问密钥派生的 Authorization 标头值的部分。您应该将自己的秘密访问密钥和
虚拟主机形式下的存储桶名称分别替换为下面的“_S3_SECRET”和“_S3_BUCKET_NAME”。
import base64
from datetime import datetime
import hmac
import sha
_S3_SECRET = "my-s3-secret"
_S3_BUCKET_NAME = "my-bucket-name"
def get_upload_header_values(content_type, filename):
now = datetime.utcnow()
date_string = now.strftime("%a, %d %b %Y %H:%M:%S +0000")
full_pathname = '/%s/%s' % (_S3_BUCKET_NAME, filename)
string_to_sign = "PUT\n\n%s\n%s\n%s" % (
content_type, date_string, full_pathname)
h = hmac.new(_S3_SECRET, string_to_sign, sha)
auth_string = base64.encodestring(h.digest()).strip()
return (date_string, auth_string)
使用文件名
foo.txt
和内容类型
text/plain
调用此函数会产生以下结果:
>>> get_upload_header_values('text/plain', 'foo.txt')
('Wed, 06 Feb 2013 00:57:45 +0000', 'EUSj3g70aEsEqSyPT/GojZmY8eI=')
请注意,如果您运行此代码,则返回的时间将不同,因此编码的HMAC摘要也将不同。
现在,iPhone客户端只需使用返回的日期和HMAC摘要向S3发出PUT请求。 假设
- 服务器在名为
serverJson
的某个JSON对象中返回了上面的date_string
和auth_string
- 您的S3访问密钥(而不是仅在服务器上的秘密)被命名为
kS3AccessKey
- 您的S3存储桶名称(设置为上面的
my-bucket-name
)被命名为kS3BucketName
- 文件内容被编组到名为
data
的NSData
对象中
- 发送到服务器的文件名是一个名为
filename
的字符串
- 发送到服务器的内容类型是一个名为
contentType
的字符串
然后,您可以执行以下操作创建
NSURLRequest
:
NSString *serverDate = [serverJson objectForKey:@"date"]
NSString *serverHmacDigest = [serverJson objectForKey:@"hmacDigest"]
NSMutableDictionary *headers = [[NSMutableDictionary alloc] init];
[headers setObject:contentType forKey:@"Content-Type"];
NSString *host = [NSString stringWithFormat:@"%@.s3.amazonaws.com", kS3BucketName]
[headers setObject:host forKey:@"Host"];
[headers setObject:serverDate forKey:@"Date"];
NSString *authorization = [NSString stringWithFormat:@"AWS %@:%@", kS3AccessKey, serverHmacDigest];
[headers setObject:authorization forKey:@"Authorization"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setAllHTTPHeaderFields:headers];
[request setHTTPBody:data];
[request setHTTPMethod:@"PUT"];
NSString *postUrl = [NSString stringWithFormat:@"http://%@.s3.amazonaws.com/%@", kS3BucketName, filename];
[request setURL:[NSURL URLWithString:postUrl]];
接下来,您可以发出请求。如果您正在使用优秀的AFNetworking库,那么您可以使用XMLDocumentRequestOperationWithRequest:success:failure:
将request
包装在一个AFXMLRequestOperation
对象中,然后调用其start
方法。完成后不要忘记释放headers
和request
。
请注意,客户端从服务器获取了Date
头的值。这是因为,正如亚马逊在"时间戳要求"下所描述的那样:
“对于已认证的请求,必须使用有效的时间戳(使用HTTP日期头或x-amz-date替代方案)。此外,包含在已认证请求中的客户端时间戳必须在收到请求时与Amazon S3系统时间相差不超过15分钟。否则,请求将失败并显示RequestTimeTooSkewed错误状态代码。”
“因此,不要依赖于客户端时间是否正确以使请求成功,而是依赖于服务器,服务器应该使用NTP(和类似ntpd的守护程序)。 ”
PUT
方法,并委托服务器生成需要密钥的Authorization
头部分。这样,您就不会冒着将访问密钥暴露给越狱iPhone用户的风险,同时也不会将上传文件的负担放在服务器上。 - shadowmatter