使用 React 将文件上传到 AWS S3 时使用进度处理程序
Using progress handler when uploading files to AWS S3 with React
我最近才处理 AWS SDK,因此如果我的方法完全是胡说八道,请原谅。
我想将一个简单的媒体文件上传到我的 S3。我正在关注 this tutorial and so far I am able to upload files without a problem. For userbility a progress bar would be a nice extra and therefore I was researching how to achieve this. I quickly found that the but we should use @aws-sdk/lib-storage
。使用这个库,我仍然可以将文件上传到 S3,但我无法让进度跟踪器工作! 我认为这与我没有完全理解如何在 React 组件中处理 async
有关。
所以这是我的缩小组件示例(我在这里使用 Chakra UI)
const TestAWS: React.FC = () => {
const inputRef = useRef<HTMLInputElement | null>(null);
const [progr, setProgr] = useState<number>();
const region = "eu-west-1";
const bucketname = "upload-test";
const handleClick = async () => {
inputRef.current?.click();
};
const handleChange = (e: any) => {
console.log('Start file upload');
const file = e.target.files[0];
const target = {
Bucket: bucketname,
Key: `jobs/${file.name}`,
Body: file,
};
const s3 = new S3Client({
region: region,
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient({ region: region }),
identityPoolId: "---MY ID---",
}),
});
const upload = new Upload({
client: s3,
params: target,
});
const t = upload.on("httpUploadProgress", progress => {
console.log("Progress", progress);
if (progress.loaded && progress.total) {
console.log("loaded/total", progress.loaded, progress.total);
setProgr(Math.round((progress.loaded / progress.total) * 100)); // I was expecting this line to be sufficient for updating my component
}
});
await upload.done().then(r => console.log(r));
};
console.log('Progress', progr);
return (
<InputGroup onClick={handleClick}>
<input ref={inputRef} type={"file"} multiple={false} hidden accept='video/*' onChange={e => handleChange(e)} />
<Flex layerStyle='uploadField'>
<Center w='100%'>
<VStack>
<PlusIcon />
<Text>Choose Video File</Text>
</VStack>
</Center>
</Flex>
{progr && <Progress value={progr} />}
</InputGroup>
);
};
export default TestAWS;
所以基本上我看到事件被触发 (开始文件上传)。然后需要一段时间,我在控制台中看到了 Promise 结果和 Progress, 100
。这对我来说意味着状态变量得到更新(至少一次)但是组件没有重新渲染?
我在这里做错了什么?任何帮助表示赞赏!
好的,我已经找到解决办法了。 state 变量的回调工作正常并且做了它应该做的事情。但是 Upload
对象的配置已关闭。在深入研究源代码后,我发现事件侦听器只有在 上传者上传了更多数据 时才会被触发。因为 Uploader 将上传分块,所以您有两个单独的配置参数,它们允许您将上传分成单独的块。所以
const upload = new Upload({
client: s3,
params: target,
queueSize: 4, // 4 is minimum
partSize: 5*1024*1024 // 5MB is minimum
});
当我们上传的文件 大于 5MB 时,基本上会完成这项工作! 只有这样事件才会再次触发并更新状态变量。
因为这个上传器是为处理大文件上传而设计的,这完全是有道理的,我们可以根据我们要上传的文件简单地调整queueSize
和partSize
。像
let queueSize = 10;
const file = event.target.files[0];
let partSize = file.size / (10 * 1024 * 1024); // 1/10th of the file size in MB
const upload = new Upload({
client: s3,
params: target,
queueSize: partSize > 5 queueSize : undefined,
partSize: partSize > 5 ? partsize : undefined
});
显然,这可以做得更复杂,但我不想花太多时间在这上面,因为它不是原始问题的一部分。
结论
如果您的文件足够大 (>5MB),您将看到进度更新,具体取决于您选择拆分的块数(5MB 或更多)文件。
因为这只影响原始示例中的 handleChange
方法,我 post 为了完整性
const handleChange = async ( event ) => {
const file = event.target.files[0]
const target = {
Bucket: 'some-S3-bucket',
Key: `jobs/${file.name}`,
Body: file,
};
const s3 = new S3Client({
region: 'your-region',
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient({ region: 'your-region' }),
identityPoolId: "your-id",
}),
});
// this will default to queueSize=4 and partSize=5MB
const upload = new Upload({
client: s3,
params: target
});
upload.on("httpUploadProgress", progress => {
console.log('Current Progress', progress);
setProgr(progress);
});
await upload.done().then(r => console.log(r));
}
也许这对遇到同样问题的人有帮助。
今天遇到完全相同的问题(使用 Vue)后,我看到了您的回答!
确实你是对的:AWS SDK JS v3 事件只在每个 part 触发,这一点都不清楚,我也浪费时间调试它。就像 4MB 的文件一样,它只会以 100% 的速度触发。
如您所说,您可以试验部分大小 ,但 最小值为 5MB 等连接速度较慢时,我发现上传可能会像您一样卡住等待 5MB 获取 any 数据。唔。所以我所做的是查看正在上传的文件的大小。如果它低于阈值(比如 25MB,或任何适用的大小),那么一次上传所有内容可能是安全的,因为您实际上并不需要分段上传。所以我 也 做了一个预签名的 URL (https://aws.amazon.com/blogs/developer/generate-presigned-url-modular-aws-sdk-javascript/) 可以用来使用 axios PUT (因为 fetch
还不支持进度事件).
这样您就可以对大文件使用 upload
(您实际上需要分块上传并且 5MB 占文件大小的百分比很小),并使用预先签名的 URL小文件,因此更新频率更高。
两者可以使用相同的进度事件处理程序。
this.$axios
.request({
method: "PUT",
url: SIGNED-URL-HERE,
data: file,
timeout: 3600 * 1000,
onUploadProgress: this.uploadProgress,
})
.then((data) => {
console.log("Success", data);
})
.catch((error) => {
console.log("Error", error.code, error.message);
});
不理想,但有帮助。
我最近才处理 AWS SDK,因此如果我的方法完全是胡说八道,请原谅。
我想将一个简单的媒体文件上传到我的 S3。我正在关注 this tutorial and so far I am able to upload files without a problem. For userbility a progress bar would be a nice extra and therefore I was researching how to achieve this. I quickly found that the @aws-sdk/lib-storage
。使用这个库,我仍然可以将文件上传到 S3,但我无法让进度跟踪器工作! 我认为这与我没有完全理解如何在 React 组件中处理 async
有关。
所以这是我的缩小组件示例(我在这里使用 Chakra UI)
const TestAWS: React.FC = () => {
const inputRef = useRef<HTMLInputElement | null>(null);
const [progr, setProgr] = useState<number>();
const region = "eu-west-1";
const bucketname = "upload-test";
const handleClick = async () => {
inputRef.current?.click();
};
const handleChange = (e: any) => {
console.log('Start file upload');
const file = e.target.files[0];
const target = {
Bucket: bucketname,
Key: `jobs/${file.name}`,
Body: file,
};
const s3 = new S3Client({
region: region,
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient({ region: region }),
identityPoolId: "---MY ID---",
}),
});
const upload = new Upload({
client: s3,
params: target,
});
const t = upload.on("httpUploadProgress", progress => {
console.log("Progress", progress);
if (progress.loaded && progress.total) {
console.log("loaded/total", progress.loaded, progress.total);
setProgr(Math.round((progress.loaded / progress.total) * 100)); // I was expecting this line to be sufficient for updating my component
}
});
await upload.done().then(r => console.log(r));
};
console.log('Progress', progr);
return (
<InputGroup onClick={handleClick}>
<input ref={inputRef} type={"file"} multiple={false} hidden accept='video/*' onChange={e => handleChange(e)} />
<Flex layerStyle='uploadField'>
<Center w='100%'>
<VStack>
<PlusIcon />
<Text>Choose Video File</Text>
</VStack>
</Center>
</Flex>
{progr && <Progress value={progr} />}
</InputGroup>
);
};
export default TestAWS;
所以基本上我看到事件被触发 (开始文件上传)。然后需要一段时间,我在控制台中看到了 Promise 结果和 Progress, 100
。这对我来说意味着状态变量得到更新(至少一次)但是组件没有重新渲染?
我在这里做错了什么?任何帮助表示赞赏!
好的,我已经找到解决办法了。 state 变量的回调工作正常并且做了它应该做的事情。但是 Upload
对象的配置已关闭。在深入研究源代码后,我发现事件侦听器只有在 上传者上传了更多数据 时才会被触发。因为 Uploader 将上传分块,所以您有两个单独的配置参数,它们允许您将上传分成单独的块。所以
const upload = new Upload({
client: s3,
params: target,
queueSize: 4, // 4 is minimum
partSize: 5*1024*1024 // 5MB is minimum
});
当我们上传的文件 大于 5MB 时,基本上会完成这项工作! 只有这样事件才会再次触发并更新状态变量。
因为这个上传器是为处理大文件上传而设计的,这完全是有道理的,我们可以根据我们要上传的文件简单地调整queueSize
和partSize
。像
let queueSize = 10;
const file = event.target.files[0];
let partSize = file.size / (10 * 1024 * 1024); // 1/10th of the file size in MB
const upload = new Upload({
client: s3,
params: target,
queueSize: partSize > 5 queueSize : undefined,
partSize: partSize > 5 ? partsize : undefined
});
显然,这可以做得更复杂,但我不想花太多时间在这上面,因为它不是原始问题的一部分。
结论
如果您的文件足够大 (>5MB),您将看到进度更新,具体取决于您选择拆分的块数(5MB 或更多)文件。
因为这只影响原始示例中的 handleChange
方法,我 post 为了完整性
const handleChange = async ( event ) => {
const file = event.target.files[0]
const target = {
Bucket: 'some-S3-bucket',
Key: `jobs/${file.name}`,
Body: file,
};
const s3 = new S3Client({
region: 'your-region',
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient({ region: 'your-region' }),
identityPoolId: "your-id",
}),
});
// this will default to queueSize=4 and partSize=5MB
const upload = new Upload({
client: s3,
params: target
});
upload.on("httpUploadProgress", progress => {
console.log('Current Progress', progress);
setProgr(progress);
});
await upload.done().then(r => console.log(r));
}
也许这对遇到同样问题的人有帮助。
今天遇到完全相同的问题(使用 Vue)后,我看到了您的回答!
确实你是对的:AWS SDK JS v3 事件只在每个 part 触发,这一点都不清楚,我也浪费时间调试它。就像 4MB 的文件一样,它只会以 100% 的速度触发。
如您所说,您可以试验部分大小 ,但 最小值为 5MB 等连接速度较慢时,我发现上传可能会像您一样卡住等待 5MB 获取 any 数据。唔。所以我所做的是查看正在上传的文件的大小。如果它低于阈值(比如 25MB,或任何适用的大小),那么一次上传所有内容可能是安全的,因为您实际上并不需要分段上传。所以我 也 做了一个预签名的 URL (https://aws.amazon.com/blogs/developer/generate-presigned-url-modular-aws-sdk-javascript/) 可以用来使用 axios PUT (因为 fetch
还不支持进度事件).
这样您就可以对大文件使用 upload
(您实际上需要分块上传并且 5MB 占文件大小的百分比很小),并使用预先签名的 URL小文件,因此更新频率更高。
两者可以使用相同的进度事件处理程序。
this.$axios
.request({
method: "PUT",
url: SIGNED-URL-HERE,
data: file,
timeout: 3600 * 1000,
onUploadProgress: this.uploadProgress,
})
.then((data) => {
console.log("Success", data);
})
.catch((error) => {
console.log("Error", error.code, error.message);
});
不理想,但有帮助。