Korzystanie z obsługi postępu podczas ładowania plików w AWS S3 za pomocą React

0

Pytanie

Ja dopiero niedawno zacząłem pracować z AWS SDK, i dlatego, proszę, przepraszam, jeśli moje podejście jest kompletna bzdurę.

Chcę pobrać prosty plik multimedialny na swój S3. Śledzę tej instrukcji i nadal mogę pobierać pliki bez problemów. Dla wygody użytkowania wskaźnik postępu byłby dobrym dodatkiem, więc uczyłem się, jak to osiągnąć. Szybko okazało się, że bieżący AWS SDK v3 nie obsługuje httpUploadProgress więcej, ale musimy wykorzystać@aws-sdk/lib-storage zamiast. Za pomocą tej biblioteki, nadal mogę pobierać pliki w S3, ale nie mogę zmusić do pracy tracker postępu! Zakładam, że ma to coś wspólnego z tym, że ja nie do końca rozumiem, jak walczyć z async w składniku reakcji.

Tak więc, oto mój obniżonego przykład komponentu (tutaj używam interfejs użytkownika 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;

W ten sposób, w zasadzie widzę, jak odbywa się wydarzenie (rozpoczęcie pobierania pliku). Następnie przechodzi przez jakiś czas i widzę obiecany wynik i Progress, 100 w mojej konsoli. Dla mnie to oznacza, że zmienna stanu aktualizowana (co najmniej raz), ale składnik nie przerysowane?

O co chodzi, co ja tu robię nie tak? Każda pomoc będzie wdzięczna!

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

Najlepsza odpowiedź

1

Ok, znalazłem rozwiązanie. Wywołanie zmiennej stanu działa normalnie i robi to, co powinien. Ale konfiguracja Upload obiekt został wyłączony. Kopanie w źródle, okazało się, że detektor zdarzeń uruchamia się tylko wtedy, jeśli bootloader wgrany więcej danych. Ponieważ bootloader фрагментирует pliki do pobrania, pliki, masz dwa oddzielne opcji konfiguracji, które pozwalają podzielić pobieranie na poszczególne fragmenty. Tak

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

głównie wykonuje tę pracę, gdy plik do pobrania przekracza 5 MB! Tylko po to zdarzenie uruchamia się ponownie i aktualizuje zmienną stanu.

Ponieważ ten downloader jest przeznaczony do obsługi dużych plików do pobrania, to ma sens, i moglibyśmy po prostu dostosować queueSize i partSize zgodnie z plikiem, który chcemy pobrać. Coś takiego

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
});

Oczywiście, że można to zrobić o wiele trudniejsze, ale nie chciałem tracić na to zbyt dużo czasu, ponieważ to nie wchodzi w początkowe pytanie.

Wniosek

Jeśli twój plik jest dość duży (>5 MB), pojawi się aktualizacja postępu w zależności od liczby segmentów (5 MB lub więcej), które wybrałeś do podziału pliku.

Ponieważ ma to wpływ tylko na handleChange metoda z oryginalnego przykład publikuję to dla kompletności

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));
} 

Może to pomoże komuś, kto ma taki sam problem.

2021-11-22 18:06:15
1

Natknąłem się na odpowiedź po tym, jak miałem dokładnie taki sam problem (z Vue) dziś!

Rzeczywiście, masz rację: zdarzenie AWS SDK JS v3 działa tylko dla jednej części, co nie jest jasne, i ja też spędziłem czasu na debugowanie. Jak dla pliku o wielkości 4 MB, to będzie działać tylko na 100%.

Jak powiedział, można poeksperymentować z wielkością części, ale minimalny rozmiar wynosi 5 MB, a więc przy wolnym połączeniu, które znalazłem, może się wydawać, że pobieranie zacięcie, tak jak trzeba poczekać 5 MB, aby uzyskać jakiekolwiekalbo dane. Hmm. Dlatego spojrzałem na rozmiar pliku. I jeśli znajduje się on poniżej wartości progowej (powiedzmy, 25 MB lub coś innego dotyczy), cóż, pewnie, bezpiecznie pobrać wszystko to za jednym razem, tak jak w rzeczywistości nie jest wam potrzebna pobieranie z kilku części. I dlatego ja również zrobiłem URL podpisane przez przewodniczącego (https://aws.amazon.com/blogs/developer/generate-presigned-url-modular-aws-sdk-javascript/), który można użyć, aby umieścić za pomocą axios (tak jak fetch na razie nie obsługuje zdarzenia postępu).

W ten sposób można korzystać z upload dla dużych plików (gdzie naprawdę jest potrzebna pobieranie z kilku części i gdzie 5 MB w procentach od rozmiaru pliku niewielkie), a także użyć adresu URL z wskaźnikiem dla małych plików i w ten sposób otrzymywać znacznie bardziej częste aktualizacje.

Jeden i ten sam program obsługi zdarzeń wykonania może być oba.

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);
  });

Nie jest idealnie, ale to pomaga.

2021-11-24 00:54:55

U mnie był ten sam pomysł, ale szczerze mówiąc, myślę lib-storage nigdy nie był przeznaczony do zastosowania w małych plików do pobrania. Niestety, wygląda na to, że obecnie nie ma zadowalającego rozwiązania przy użyciu v3 (ponieważ wykorzystuje api próbki pod maską) i pobierania małych plików. W ten sposób, twoje podejście jest z pewnością dobrym obejściem drogą, ale mam nadzieję, że wkrótce coś wprowadzenie w SDK.
Flo Ragossnig

Zgadzam się. Irytuje konieczność stosowania podpisany adres URL w celu obejścia tego problemu, ale jeśli/tak długo, aż SDK nie zmieni się (być może, gdy API próbki doda postęp pobierania), w tej chwili, jak się wydaje, trzeba wybrać w zależności od tego, czy są złożone lub regularne aktualizacje postępu najważniejsze dla twojego użytku
coder_uk

W innych językach

Ta strona jest w innych językach

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