Использование обработчика прогресса при загрузке файлов в AWS S3 с помощью React

0

Вопрос

Я только недавно начал работать с AWS SDK, и поэтому, пожалуйста, извините, если мой подход является полной чепухой.

Я хочу загрузить простой медиафайл на свой S3. Я следовал этому руководству и до сих пор могу загружать файлы без проблем. Для удобства использования индикатор прогресса был бы хорошим дополнением, и поэтому я изучал, как этого достичь. Я быстро обнаружил, что текущий AWS SDK v3 не поддерживает httpUploadProgress больше, но мы должны использовать@aws-sdk/lib-storage вместо. Используя эту библиотеку, я все еще могу загружать файлы в S3, но я не могу заставить работать трекер прогресса! Я предполагаю, что это как-то связано с тем, что я не до конца понимаю, как бороться с async в компоненте реакции.

Итак, вот мой уменьшенный пример компонента (здесь я использую пользовательский интерфейс Chakra)

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;

Таким образом, в основном я вижу, как происходит событие (запуск загрузки файла). Затем проходит некоторое время, и я вижу обещанный результат и Progress, 100 в моей консоли. Для меня это означает, что переменная состояния обновляется (по крайней мере, один раз), но компонент не перерисовывается?

В чем дело, что я здесь делаю не так? Любая помощь будет признательна!

amazon-s3 aws-sdk reactjs
2021-11-22 15:34:31
2

Лучший ответ

1

Хорошо, я нашел решение. Обратный вызов переменной состояния работает нормально и делает то, что должен. Но конфигурация Upload объект был выключен. Покопавшись в источнике, я обнаружил, что прослушиватель событий запускается только в том случае, если загрузчик загрузил больше данных. Поскольку загрузчик фрагментирует загружаемые файлы, у вас есть два отдельных параметра конфигурации, которые позволяют вам разделить загрузку на отдельные фрагменты. Так

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

в основном выполняет эту работу, когда загружаемый файл превышает 5 МБ! Только после этого событие запускается снова и обновляет переменную состояния.

Поскольку этот загрузчик предназначен для обработки больших загрузок файлов, это имеет смысл, и мы могли бы просто настроить 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
});

Очевидно, что это можно сделать гораздо сложнее, но я не хотел тратить на это слишком много времени, так как это не входит в первоначальный вопрос.

Вывод

Если ваш файл достаточно велик (>5 МБ), вы увидите обновление хода выполнения в зависимости от количества фрагментов (5 МБ или более), которые вы выбрали для разделения файла.

Поскольку это влияет только на 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));
} 

Может быть, это поможет кому-то, у кого такая же проблема.

2021-11-22 18:06:15
1

Я наткнулся на ваш ответ после того, как у меня была точно такая же проблема (с Vue) сегодня!

Действительно, вы правы: событие AWS SDK JS v3 срабатывает только для одной части, что совсем не ясно, и я тоже потратил время на отладку. Как и для файла размером 4 МБ, он будет срабатывать только на 100%.

Как вы сказали, вы можете поэкспериментировать с размером детали, но минимальный размер составляет 5 МБ, и поэтому при медленном подключении, которое я обнаружил, может показаться, что загрузка застряла, так как вам нужно подождать 5 МБ, чтобы получить какие-либо данные. Хмм. Поэтому я посмотрел на размер загружаемого файла. И если он находится ниже порогового значения (скажем, 25 МБ или что-то еще применимо), что ж, вероятно, безопасно загрузить все это за один раз, так как вам на самом деле не нужна загрузка из нескольких частей. И поэтому я также сделал URL с подписью председателя (https://aws.amazon.com/blogs/developer/generate-presigned-url-modular-aws-sdk-javascript/) который можно использовать для размещения с помощью axios (так как fetch пока не поддерживает события прогресса).

Таким образом, вы можете использовать upload для больших файлов (где вам на самом деле нужна загрузка из нескольких частей и где 5 МБ в процентах от размера файла невелики), а также используйте 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);
  });

Не идеально, но это помогает.

2021-11-24 00:54:55

У меня была та же идея, но, честно говоря, я думаю lib-storage никогда не предназначался для использования для небольших загрузок файлов. К сожалению, похоже, что в настоящее время нет удовлетворительного решения при использовании v3 (поскольку он использует api выборки под капотом) и загрузке небольших файлов. Таким образом, ваш подход, безусловно, является хорошим обходным путем, но, надеюсь, они очень скоро что-то внедрят в SDK.
Flo Ragossnig

Я согласен. Раздражает необходимость использовать подписанный URL-адрес в качестве обходного пути, но если/до тех пор, пока SDK не изменится (возможно, когда API выборки добавит прогресс загрузки), на данный момент, похоже, вам придется выбирать в зависимости от того, являются ли составные или регулярные обновления прогресса наиболее важными для вашего использования
coder_uk

На других языках

Эта страница на других языках

Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................