使用 React 将文件上传到 AWS S3 时使用进度处理程序

Flo*_*nig 0 amazon-s3 reactjs aws-sdk

我最近才接触AWS SDK,因此如果我的方法完全是胡说八道,请原谅。

我想将一个简单的媒体文件上传到我的 S3。我正在遵循本教程,到目前为止我可以毫无问题地上传文件。对于用户性来说,进度条将是一个很好的额外功能,因此我正在研究如何实现这一点。我很快发现当前的 AWS SDK v3不再支持httpUploadProgress但我们应该使用@aws-sdk/lib-storage。使用这个库,我仍然可以将文件上传到 S3,但我无法让进度跟踪器工作!我认为这与我没有完全理解如何async在 React 组件中进行处理有关。

这是我的缩小组件示例(我在这里使用 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;
Run Code Online (Sandbox Code Playgroud)

所以基本上我看到事件被触发(开始文件上传)。然后需要一段时间,我才能Progress, 100在控制台中看到 Promise 结果和 。这对我来说意味着状态变量被更新(至少一次)但组件不会重新渲染?

我在这里做错了什么?任何帮助表示赞赏!

Flo*_*nig 5

好吧,我已经找到解决方案了。状态变量上的回调工作正常并且执行其应该执行的操作。但对象的配置Upload已关闭。在深入研究源代码后,我发现只有当上传者上传了更多数据时才会触发事件侦听器。因为上传器会对上传进行分块,所以您有两个单独的配置参数,它们允许您将上传分成单独的块。所以

const upload = new Upload({
  client: s3,
  params: target,
  queueSize: 4,          // 4 is minimum
  partSize: 5*1024*1024  // 5MB is minimum
});
Run Code Online (Sandbox Code Playgroud)

基本上当我们上传的文件大于 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
});

Run Code Online (Sandbox Code Playgroud)

显然,这可以做得更复杂,但我不想在这上面花太多时间,因为它不是原始问题的一部分。

结论

如果您的文件足够大(> 5MB),您将看到进度更新,具体取决于您选择分割文件的块数(5MB 或更多)。

由于这仅影响handleChange原始示例中的方法,因此为了完整性我发布此内容

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));
} 
Run Code Online (Sandbox Code Playgroud)

也许这可以帮助有同样问题的人。