Загрузите s3 с помощью curl, используя предварительно подписанный URL (получение 403)
Я использую curl для вызова API Java ReST для получения URL-адреса. Затем Java генерирует предварительно подписанный URL-адрес для загрузки S3 с использованием моих учетных данных S3 и возвращает это в ответ ReST. Curl берет URL-адрес и использует его для загрузки на S3, но S3 возвращает 403 "Подписанная вами подпись запроса не соответствует предоставленной вами подписи. Проверьте свой ключ и метод подписи".
Вот код, который я использую для создания предварительно подписанного URL:
public class S3Util {
static final AmazonS3 s3 = new AmazonS3Client( new AWSCredentials() {
@Override
public String getAWSAccessKeyId() {
return "XXXXXXX";
}
@Override
public String getAWSSecretKey() {
return "XXXXXXXXXXXXXX";
}
});
static final String BUCKET = "XXXXXXXXXXXXXXXXXXXXXXXXXXX";
static public URL getMediaChunkURL( MediaChunk mc, HttpMethod method ) {
String key = ...
//way in the future (for testing)...
Date expiration = new Date( System.currentTimeMillis() + CalendarUtil.ONE_MINUTE_IN_MILLISECONDS*60*1000 );
GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(BUCKET, key, method);
req.setExpiration(expiration);
req.addRequestParameter("Content-Type", "application/octet-stream");
//this gets passed to the end user:
return s3.generatePresignedUrl(req);
}
}
и в curl, запустите от bash, я выполняю это:
echo Will try to upload chunk to ${location}
curl -i -X POST \
-F 'Content-Type=application/octet-stream' \
-F "[email protected]${fileName}" \
${location} || (echo upload chunk failed. ; exit 1 )
Помимо прочего, я попробовал PUT, и я попробовал "Content-type" (нижний регистр T). Я понимаю, что я что-то пропустил (или что-то подобное), но после прочтения соответствующих документов, поиска в Google и просмотра многих подобных вопросов, я не уверен, что это такое. Я вижу много подсказок о необходимых заголовках, но я думал, что отложенный URL должен был устранить эти потребности. Может быть, нет?
ТИА!
Update:
Просто, чтобы быть ясным, я тестировал загрузки, и это работает нормально.
Java выглядит так:
GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(BUCKET, key, HttpMethod.GET);
req.setExpiration(expiration);
и curl просто:
curl -i ${location}
Ответы
Ответ 1
Я смог создать предварительно подписанный URL через С# и загрузить его после curl, как и ожидалось. Учитывая мои тесты, я подозреваю, что вы действительно не используете curl правильно - я смог загрузить файл следующим образом:
curl -v --upload-file ${fileName} ${location}
Параметр -v
сбрасывает заголовки запроса и ответа (а также SSL-квитирование) для целей отладки и иллюстрации:
> PUT [...] HTTP/1.1
> User-Agent: curl/7.21.0 [...]
> Host: [...]
> Accept: */*
> Content-Length: 12
> Expect: 100-continue
Обратите внимание, что --upload-file
(или -T
) облегчает PUT
, как ожидалось, но при необходимости добавляет больше заголовков, что дает правильный ответ в ответ:
< HTTP/1.1 100 Continue
< HTTP/1.1 200 OK
< x-amz-id-2: [...]
< x-amz-request-id: [...]
< Date: Tue, 31 Jan 2012 18:34:56 GMT
< ETag: "253801c0d260f076b0d5db5b62c54824"
< Content-Length: 0
< Server: AmazonS3
Ответ 2
когда вы делаете это с помощью curl, вам нужно поместить url в одинарные кавычки, иначе половина строки запроса будет отрублена (часть с ключом/сигнатурой).
Ответ 3
Способ создания URL-адреса:
private static URL generateRUL(String objectKey, String ACCESS_KEY, String SECRET_KEY, String BUCKET_NAME) {
AmazonS3 s3Client = new AmazonS3Client(new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY));
URL url = null;
try {
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(BUCKET_NAME, objectKey);
request.setMethod(com.amazonaws.HttpMethod.PUT);
request.setExpiration(new Date( System.currentTimeMillis() + (60 * 60 * 1000)));
// Very important ! It won't work without adding this!
// And request.addRequestParameter("Content-Type", "application/octet-stream") won't work neither
request.setContentType("application/octet-stream");
url = s3Client.generatePresignedUrl(request );
} catch (AmazonServiceException exception) {
} catch (AmazonClientException ace) { }
return url;
}
Способ загрузки файла:
public int upload(byte[] fileBytes, URL url) {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("PUT");
connection.setRequestProperty("Content-Type", "application/octet-stream"); // Very important ! It won't work without adding this!
OutputStream output = connection.getOutputStream();
InputStream input = new ByteArrayInputStream(fileBytes);
byte[] buffer = new byte[4096];
int length;
while ((length = input.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
output.flush();
return connection.getResponseCode();
}
Ответ 4
Несмотря на то, что GeneratePresignedUrlRequest
принимает аргумент метода http (и имеет функцию setMethod
), он кажется непригодным для чего-либо, кроме GET.
http://wiki.nercomp.org/wiki/images/0/05/AmazonWebServices.pdf утверждает: "Практика подписания запроса и передачи его третьей стороне для исполнения подходит только для простого объекта GET Запросы." Возможно, для чего-то можно использовать другой метод, но, видимо, не это.
Итак, вместо этого мне пришлось следовать инструкциям здесь:
http://aws.amazon.com/articles/1434?_encoding=UTF8&jiveRedirect=1
Это сложнее, потому что клиент должен опубликовать полную форму, а не просто использовать URL-адрес, а также означает, что вся информация об этом должна быть передана клиенту отдельно, но, похоже, она работает.