使用预签名 url 的分段上传 - Scaleway S3 兼容 object 存储
Multipart upload with presigned urls - Scaleway S3-compatible object storage
我正在尝试在 Scaleway Object 存储(S3 兼容)上使用预先签名的 urls 进行分段上传,我在浏览器生成的预检请求中遇到错误 (403)但我的 CORS 设置似乎设置正确。 (基本上通配符允许 headers 和起源)。
错误带有 403 状态代码,如下所示:
<?xml version='1.0' encoding='UTF-8'?>
<Error><Code>AccessDenied</Code><Message>Access Denied.</Message><RequestId>...</RequestId></Error>
我在这个问题上停留了一段时间,我尝试从我的浏览器复制 pre-flight 请求以在其他地方重现它并尝试稍微调整一下。
从 pre-flight 请求的 url 中删除查询参数使请求成功(returns 200,Access-Control-Allow-*
响应 headers 正确设置)但这显然是不是浏览器行为...
这不起作用(秘密、密钥和名称已更改)
curl 'https://bucket-name.s3.fr-par.scw.cloud/tmp-screenshot-2021-01-20-at-16-21-33.png?AWSAccessKeyId=XXXXXXXXXXXXXXXXXXXX&Expires=1638217988&Signature=NnP1XLlcvPzZnsUgDAzm1Uhxri0%3D&partNumber=1&uploadId=OWI1NWY5ZGrtYzE3MS00MjcyLWI2NDAtNjFkYTM1MTRiZTcx' -X OPTIONS -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:94.0) Gecko/20100101 Firefox/94.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Referer: http://domain.tech/' -H 'Access-Control-Request-Method: PUT' -H 'Origin: http://domain.tech' -H 'DNT: 1' -H 'Connection: keep-alive' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: no-cors' -H 'Sec-Fetch-Site: cross-site' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache'
This Works(秘密、密钥和名称已更改)
curl 'https://bucket-name.s3.fr-par.scw.cloud/tmp-screenshot-2021-01-20-at-16-21-33.png' -X OPTIONS -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:94.0) Gecko/20100101 Firefox/94.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Referer: http://domain.tech/' -H 'Access-Control-Request-Method: PUT' -H 'Origin: http://domain.tech' -H 'DNT: 1' -H 'Connection: keep-alive' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: no-cors' -H 'Sec-Fetch-Site: cross-site' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache'
url 来自 aws-sdk
并以这种方式生成:
const S3Client = new S3({
credentials: {
accessKeyId: env.SCW_ACCESS_KEY,
secretAccessKey: env.SCW_SECRET_KEY,
},
endpoint: `https://s3.${env.SCW_REGION}.scw.cloud`,
})
S3Client.getSignedUrlPromise('uploadPart', {
Bucket: bucket,
Key: key,
UploadId: multipartUpload.UploadId,
PartNumber: idx + 1,
})
并在前端使用这种方式:
// url being the url generated in backend as demonstrated above
const response = await fetch(url, {
method: 'PUT',
body: filePart,
signal: abortController.signal,
})
如果有人能帮我解决这个或那个,那就太好了!
事实证明,在这种情况下,Scaleway Object 存储不完全兼容 S3。
这是一个解决方法:
- 安装
aws4
库以轻松签署请求(或按照 this scaleway doc 手动签署您的请求)
- 完全按照 this other scaleway doc 中的规定形成您的请求(这是
aws-sdk
行为不同的地方,它生成 url 和 AWSAccessKeyId
,Expires
和 Signature
导致 scaleway API 失败的查询参数。Scaleway API 只需要 partNumber
和 uploadId
).
- Return 生成的 url 和 headers 到前端
// Backend code
const signedRequest = aws4.sign(
{
method: 'PUT',
path: `/${key}?partNumber=${idx + 1}&uploadId=${
multipartUpload.UploadId
}`,
service: 's3',
region: env.SCW_REGION,
host: `${bucket}.s3.${env.SCW_REGION}.scw.cloud`,
},
{
accessKeyId: env.SCW_ACCESS_KEY,
secretAccessKey: env.SCW_SECRET_KEY,
},
)
return {
url: `https://${signedRequest.host}${signedRequest.path}`,
headers: Object.keys(signedRequest.headers).map((key) => ({
key,
value: signedRequest.headers[key] as string,
})),
}
然后在前端:
// Frontend code
const headers = signedRequest.headers.reduce<Record<string, string>>(
(acc, h) => ({ ...acc, [h.key]: h.value }),
{},
)
const response = await fetch(signedRequest.url, {
method: 'PUT',
body: filePart,
headers,
signal: abortController.signal,
})
Scaleway 知道这个问题,因为我直接与他们的支持团队讨论过,他们正在努力尽可能与 S3 兼容。当您阅读本文时,此问题可能已得到解决。
感谢他们真正快速的响应时间和认真对待这件事。
我正在尝试在 Scaleway Object 存储(S3 兼容)上使用预先签名的 urls 进行分段上传,我在浏览器生成的预检请求中遇到错误 (403)但我的 CORS 设置似乎设置正确。 (基本上通配符允许 headers 和起源)。
错误带有 403 状态代码,如下所示:
<?xml version='1.0' encoding='UTF-8'?>
<Error><Code>AccessDenied</Code><Message>Access Denied.</Message><RequestId>...</RequestId></Error>
我在这个问题上停留了一段时间,我尝试从我的浏览器复制 pre-flight 请求以在其他地方重现它并尝试稍微调整一下。
从 pre-flight 请求的 url 中删除查询参数使请求成功(returns 200,Access-Control-Allow-*
响应 headers 正确设置)但这显然是不是浏览器行为...
这不起作用(秘密、密钥和名称已更改)
curl 'https://bucket-name.s3.fr-par.scw.cloud/tmp-screenshot-2021-01-20-at-16-21-33.png?AWSAccessKeyId=XXXXXXXXXXXXXXXXXXXX&Expires=1638217988&Signature=NnP1XLlcvPzZnsUgDAzm1Uhxri0%3D&partNumber=1&uploadId=OWI1NWY5ZGrtYzE3MS00MjcyLWI2NDAtNjFkYTM1MTRiZTcx' -X OPTIONS -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:94.0) Gecko/20100101 Firefox/94.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Referer: http://domain.tech/' -H 'Access-Control-Request-Method: PUT' -H 'Origin: http://domain.tech' -H 'DNT: 1' -H 'Connection: keep-alive' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: no-cors' -H 'Sec-Fetch-Site: cross-site' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache'
This Works(秘密、密钥和名称已更改)
curl 'https://bucket-name.s3.fr-par.scw.cloud/tmp-screenshot-2021-01-20-at-16-21-33.png' -X OPTIONS -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:94.0) Gecko/20100101 Firefox/94.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Referer: http://domain.tech/' -H 'Access-Control-Request-Method: PUT' -H 'Origin: http://domain.tech' -H 'DNT: 1' -H 'Connection: keep-alive' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: no-cors' -H 'Sec-Fetch-Site: cross-site' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache'
url 来自 aws-sdk
并以这种方式生成:
const S3Client = new S3({
credentials: {
accessKeyId: env.SCW_ACCESS_KEY,
secretAccessKey: env.SCW_SECRET_KEY,
},
endpoint: `https://s3.${env.SCW_REGION}.scw.cloud`,
})
S3Client.getSignedUrlPromise('uploadPart', {
Bucket: bucket,
Key: key,
UploadId: multipartUpload.UploadId,
PartNumber: idx + 1,
})
并在前端使用这种方式:
// url being the url generated in backend as demonstrated above
const response = await fetch(url, {
method: 'PUT',
body: filePart,
signal: abortController.signal,
})
如果有人能帮我解决这个或那个,那就太好了!
事实证明,在这种情况下,Scaleway Object 存储不完全兼容 S3。
这是一个解决方法:
- 安装
aws4
库以轻松签署请求(或按照 this scaleway doc 手动签署您的请求) - 完全按照 this other scaleway doc 中的规定形成您的请求(这是
aws-sdk
行为不同的地方,它生成 url 和AWSAccessKeyId
,Expires
和Signature
导致 scaleway API 失败的查询参数。Scaleway API 只需要partNumber
和uploadId
). - Return 生成的 url 和 headers 到前端
// Backend code
const signedRequest = aws4.sign(
{
method: 'PUT',
path: `/${key}?partNumber=${idx + 1}&uploadId=${
multipartUpload.UploadId
}`,
service: 's3',
region: env.SCW_REGION,
host: `${bucket}.s3.${env.SCW_REGION}.scw.cloud`,
},
{
accessKeyId: env.SCW_ACCESS_KEY,
secretAccessKey: env.SCW_SECRET_KEY,
},
)
return {
url: `https://${signedRequest.host}${signedRequest.path}`,
headers: Object.keys(signedRequest.headers).map((key) => ({
key,
value: signedRequest.headers[key] as string,
})),
}
然后在前端:
// Frontend code
const headers = signedRequest.headers.reduce<Record<string, string>>(
(acc, h) => ({ ...acc, [h.key]: h.value }),
{},
)
const response = await fetch(signedRequest.url, {
method: 'PUT',
body: filePart,
headers,
signal: abortController.signal,
})
Scaleway 知道这个问题,因为我直接与他们的支持团队讨论过,他们正在努力尽可能与 S3 兼容。当您阅读本文时,此问题可能已得到解决。 感谢他们真正快速的响应时间和认真对待这件事。