Utilizzando i progressi gestore durante il caricamento di file AWS S3 Reagire con

0

Domanda

Io sono solo di recente trattare con il AWS SDK e quindi scusate se il mio approccio è una totale assurdità.

Voglio caricare un semplice file di supporto per il mio S3. Stavo seguendo questo tutorial e finora sono in grado di caricare i file senza problemi. Per userbility una barra di avanzamento sarebbe un bel extra e quindi ero alla ricerca di come ottenere questo risultato. Ho subito scoperto che la corrente AWS SDK v3 non supporta httpUploadProgress più , ma dobbiamo usare @aws-sdk/lib-storage invece. Utilizzando questa libreria, io sono ancora in grado di caricare i file su S3, ma non può ottenere il progress tracker al lavoro! Presumo che questo ha qualcosa a che fare con me non comprendere appieno come affrontare async all'interno di un Reagire componente.

Così qui è il mio minified componente esempio (sto usando Chakra UI qui)

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;

Quindi, in pratica vedo l'evento licenziamento (start upload di file). Quindi ci vuole un po ' e vedo il risultato e la Promessa Progress, 100 nella mia console. Per me questo vuol dire che la variabile di stato viene aggiornato (almeno una volta), ma il componente non ri-renderizzare?

Che cosa è che cosa sto facendo di sbagliato? Qualsiasi aiuto è apprezzato!

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

Migliore risposta

1

Bene, ho trovato la soluzione. Richiamata la variabile di stato funziona bene e fa quello che deve. Ma la configurazione del Upload l'oggetto è stato spento. Dopo aver scavato nella fonte che ho scoperto che il listener dell'evento viene attivato solo se l'uploader ha caricato più dati. Perché Uploader blocchi l'upload si hanno due config parametri che consentono di dividere il caricamento in singoli pezzi. Così

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

fondamentalmente fa il lavoro quando il file caricati è più grande di 5MB! Solo allora l'evento viene attivato nuovamente e aggiorna la variabile di stato.

Dal momento che questo uploader è fatto per la movimentazione di grandi file di upload, questo rende totalmente senso e si potrebbe semplicemente regolare queueSize e partSize secondo il file che si desidera caricare. Qualcosa di simile

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

Ovviamente, questo può essere fatto molto più sofisticato, ma non ho voglia di spendere troppo tempo su questo in quanto non fa parte della domanda originale.

Conclusione

Se il file è abbastanza grande (>5 MB), si vedrà corso di aggiornamento, a seconda del numero di pezzi (5 mb o più) si è scelto di dividere il file.

Dal momento che questo riguarda solo il handleChange il metodo originale esempio, io di questo post per completezza

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

Forse questo aiuta qualcuno che ha lo stesso problema.

2021-11-22 18:06:15
1

Mi sono imbattuto in una tua risposta dopo che ha esattamente lo stesso problema (con Vue) oggi!

In effetti hai ragione: AWS SDK JS v3 evento viene generato solo per la parte che non è del tutto chiara e ho sprecato il tempo di debug che troppo. Come per una di 4 file, sarebbe sempre e solo il fuoco al 100%.

Come dici tu, si può sperimentare con la dimensione del pezzo , ma il minimo è di 5 mb e così su una connessione lenta che ho trovato sembra che il caricamento è attaccato come si deve attendere per 5MB per ottenere qualsiasi di dati. Hmm. Quindi quello che ho fatto è stato guardare la dimensione del file che viene caricato. E se è sotto di una soglia (diciamo 25MB, o qualsiasi altra cosa è applicabile), beh, è probabilmente sicuro per caricare tutti in una volta non si ha realmente bisogno multipart caricamento. E così ho anche fatto un presigned URL (https://aws.amazon.com/blogs/developer/generate-presigned-url-modular-aws-sdk-javascript/), che può essere utilizzato per METTERE in uso axios (dal fetch non supporta il progresso eventi di sicurezza).

Quindi, in questo modo è possibile utilizzare upload per file di grandi dimensioni (in cui è effettivamente necessario multipart caricamento e dove 5MB come percentuale della dimensione del file è di piccole dimensioni), e utilizzare un presigned URL per i file di piccole dimensioni e quindi ottenere molto più frequenti aggiornamenti.

Lo stesso progresso gestore di eventi può essere utilizzato da entrambi.

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

Non è l'ideale, ma aiuta.

2021-11-24 00:54:55

Ho avuto la stessa idea, ma ad essere onesti, credo lib-storage non è mai stato inteso per essere utilizzato per piccole upload di file. Purtroppo, sembra che non vi è attualmente alcuna soluzione soddisfacente quando si utilizza v3 (dato che usando il fetch api sotto il cofano) e il caricamento di file di piccole dimensioni. Quindi il tuo approccio è sicuramente una buona soluzione, ma speriamo che implementare qualcosa nel SDK molto presto.
Flo Ragossnig

Sono d'accordo. E ' fastidioso dover utilizzare firmato un URL come una soluzione, ma a meno che/finché l'SDK modifiche (magari quando il fetch API aggiunge il progresso del caricamento) per ora sembra che devi scegliere a seconda se multipart o regolari aggiornamenti sullo stato di avanzamento è il più importante per il vostro uso
coder_uk

In altre lingue

Questa pagina è in altre lingue

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