使用 React 预签名 url 的 AWS S3 上传空文件
AWS S3 presigned url with React uploads empty file
我正在使用 React、API 网关和 Lambda (Python) 获取预签名 url 以将文件上传到 s3 存储桶。但是上传到s3 bucket的文件总是空的(0字节)。出于某种原因,当我在我的 React 应用程序中上传 CSV 文件时。 s3中上传的文件变成了.XLS文件,但仍然命名为原来的csv。
这是我的代码:
<div>
<input type="file" name="fileOne" onChange={fileUploadOne} />
</div>
...
const fileUploadOne = (event) => {
const { files } = event.target;
getPresignedUrl(files[0]).then((response) => {
putToS3(files[0], response);
});
};
...
export async function getPresignedUrl(fileObject) {
const requestOptions = {
method: "GET",
headers: {
"Content-Type": "application/json",
},
};
const response = await fetch(
"https://<api_gateway_url>.amazonaws.com/<api_name>?filename=" +
fileObject.name +
"&filetype=" +
fileObject.type,
requestOptions
);
return await response.json();
}
export async function putToS3(fileObject, presignedUrl) {
const requestOptions = {
method: "PUT",
headers: {
"Content-Type": fileObject.type,
},
data: fileObject,
};
const response = await fetch(presignedUrl, requestOptions);
return await response;
}
我的 Lambda 代码(使用 getPresignedUrl 函数访问):
import json
import boto3
from botocore.exceptions import ClientError
def lambda_handler(event, context):
params = event["queryStringParameters"]
key = params.get('filename')
filetype = params.get('filetype')
s3_client = boto3.client('s3')
client_action = 'put_object'
bucket_name = '<bucket_name>'
resp = generate_presigned_url(
s3_client, client_action, {'Bucket': bucket_name, 'Key': key,'ContentType':filetype}, 1000)
return {
'statusCode': 200,
'headers': {
"Access-Control-Allow-Origin" : "*", # Required for CORS support to work
},
'body': json.dumps(resp)
}
def generate_presigned_url(s3_client, client_method, method_parameters, expires_in):
"""
Generate a presigned Amazon S3 URL that can be used to perform an action.
:param s3_client: A Boto3 Amazon S3 client.
:param client_method: The name of the client method that the URL performs.
:param method_parameters: The parameters of the specified client method.
:param expires_in: The number of seconds the presigned URL is valid for.
:return: The presigned URL.
"""
try:
url = s3_client.generate_presigned_url(
ClientMethod=client_method,
Params=method_parameters,
ExpiresIn=expires_in
)
print("Got presigned URL: %s", url)
except ClientError:
print(
"Couldn't get a presigned URL for client method '%s'.", client_method)
raise
return url
我发现了我的错误。我的 HTTP PUT 请求应该在 body 中有 fileObject 而不是 data.
export async function putToS3(fileObject, presignedUrl) {
const requestOptions = {
method: "PUT",
headers: {
"Content-Type": fileObject.type,
},
body: fileObject,
};
const response = await fetch(presignedUrl, requestOptions);
return await response;
}
这真的是一个非常简单和愚蠢的错误。我花了几个小时才弄明白。
我正在使用 React、API 网关和 Lambda (Python) 获取预签名 url 以将文件上传到 s3 存储桶。但是上传到s3 bucket的文件总是空的(0字节)。出于某种原因,当我在我的 React 应用程序中上传 CSV 文件时。 s3中上传的文件变成了.XLS文件,但仍然命名为原来的csv。
这是我的代码:
<div>
<input type="file" name="fileOne" onChange={fileUploadOne} />
</div>
...
const fileUploadOne = (event) => {
const { files } = event.target;
getPresignedUrl(files[0]).then((response) => {
putToS3(files[0], response);
});
};
...
export async function getPresignedUrl(fileObject) {
const requestOptions = {
method: "GET",
headers: {
"Content-Type": "application/json",
},
};
const response = await fetch(
"https://<api_gateway_url>.amazonaws.com/<api_name>?filename=" +
fileObject.name +
"&filetype=" +
fileObject.type,
requestOptions
);
return await response.json();
}
export async function putToS3(fileObject, presignedUrl) {
const requestOptions = {
method: "PUT",
headers: {
"Content-Type": fileObject.type,
},
data: fileObject,
};
const response = await fetch(presignedUrl, requestOptions);
return await response;
}
我的 Lambda 代码(使用 getPresignedUrl 函数访问):
import json
import boto3
from botocore.exceptions import ClientError
def lambda_handler(event, context):
params = event["queryStringParameters"]
key = params.get('filename')
filetype = params.get('filetype')
s3_client = boto3.client('s3')
client_action = 'put_object'
bucket_name = '<bucket_name>'
resp = generate_presigned_url(
s3_client, client_action, {'Bucket': bucket_name, 'Key': key,'ContentType':filetype}, 1000)
return {
'statusCode': 200,
'headers': {
"Access-Control-Allow-Origin" : "*", # Required for CORS support to work
},
'body': json.dumps(resp)
}
def generate_presigned_url(s3_client, client_method, method_parameters, expires_in):
"""
Generate a presigned Amazon S3 URL that can be used to perform an action.
:param s3_client: A Boto3 Amazon S3 client.
:param client_method: The name of the client method that the URL performs.
:param method_parameters: The parameters of the specified client method.
:param expires_in: The number of seconds the presigned URL is valid for.
:return: The presigned URL.
"""
try:
url = s3_client.generate_presigned_url(
ClientMethod=client_method,
Params=method_parameters,
ExpiresIn=expires_in
)
print("Got presigned URL: %s", url)
except ClientError:
print(
"Couldn't get a presigned URL for client method '%s'.", client_method)
raise
return url
我发现了我的错误。我的 HTTP PUT 请求应该在 body 中有 fileObject 而不是 data.
export async function putToS3(fileObject, presignedUrl) {
const requestOptions = {
method: "PUT",
headers: {
"Content-Type": fileObject.type,
},
body: fileObject,
};
const response = await fetch(presignedUrl, requestOptions);
return await response;
}
这真的是一个非常简单和愚蠢的错误。我花了几个小时才弄明白。