Вы можете загружать данные прямо с вашего iPhone на S3, используя REST API, и при этом сервер отвечает за генерацию части значения заголовка Authorization
, для которой требуется секретный ключ. Таким образом, вы не рискуете раскрыть ключ доступа кому-либо с взломанным iPhone, в то время как вы не несете бремени загрузки файла на сервер. Точные детали запроса на отправку можно найти в разделе «Пример объекта PUT» в разделе «Подписание и аутентификация запросов REST» . Я настоятельно рекомендую прочитать этот документ, прежде чем продолжить.
Следующий код, написанный на Python, генерирует часть значения заголовка Authorization
, полученную из вашего секретного ключа доступа S3. Вам следует заменить свой собственный секретный ключ доступа и имя корзины в форме виртуального хоста для _S3_SECRET
и _S3_BUCKET_NAME
ниже, соответственно:
import base64
from datetime import datetime
import hmac
import sha
# Replace these values.
_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 просто должен отправить запрос PUT на S3, используя возвращенную дату и дайджест HMAC. Предполагая, что
- сервер возвращает
date_string
и auth_string
выше в некотором объекте JSON с именем serverJson
- ваш ключ доступа S3 (не ваш секрет, который есть только на сервере) называется
kS3AccessKey
- имя вашей корзины S3 (установлено на
my-bucket-name
выше) с именем kS3BucketName
- содержимое файла маршалируется в
NSData
объект с именем data
- имя файла, которое было отправлено на сервер, является строкой с именем
filename
- тип содержимого, отправленный на сервер, представляет собой строку с именем
contentType
Затем вы можете сделать следующее для создания NSURLRequest
:
NSString *serverDate = [serverJson objectForKey:@"date"]
NSString *serverHmacDigest = [serverJson objectForKey:@"hmacDigest"]
// Create the headers.
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"];
// Create the request.
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 , то вы можете обернуть request
в AFXMLRequestOperation
объект, используя XMLDocumentRequestOperationWithRequest:success:failure:
, а затем вызвать его start
метод. Не забудьте отпустить headers
и request
, когда закончите.
Обратите внимание, что клиент получил значение заголовка Date
с сервера. Это связано с тем, что Amazon описывает в разделе «Требование к отметке времени» :
"Действительная метка времени (с использованием заголовка HTTP-даты или альтернативы x-amz-date) обязательна для аутентифицированных запросов. Кроме того, метка времени клиента, включенная в аутентифицированный запрос, должна находиться в пределах 15 минут от Amazon S3 системное время, когда запрос получен. Если нет, запрос завершится ошибкой с кодом состояния ошибки RequestTimeTooSkewed. "
Таким образом, вместо того, чтобы полагаться на правильное время клиента для успешного выполнения запроса, положитесь на сервер, который должен использовать NTP (и демон типа ntpd
).