如何在 Reactjs 中使用 getObject 从 AWS S3 下载文件?

How to download file from AWS S3 using getObject in Reactjs?

我正在尝试使用 React 直接从客户端将文件从 S3 存储桶下载为 .csv 格式。我已经能够毫无问题地上传文件,甚至列出存储桶中的所有文件,但在使用 getObject 实际下载文件时遇到问题。我想要的文件保存在 templates 文件夹中,因此当从下拉列表中 selected 时,我的参数中的密钥已更改并将获取该特定文件。

我曾尝试使用 Blob 来获取通过调用特定存储桶上的 getObject 和文件本身收到的 data.Body,但我遇到了一些文件无法下载或无法在 Mac OS 但在 Windows 上不起作用,或者在 Firefox 上不起作用但在 Chrome 上起作用,反之亦然。

我想知道是否有一种简单的方法可以仅使用 React 来做到这一点?获取从文件接收到的数据并将其转换为 .csv?

这是我的 Downloads.js 文件,使用 React Bootstrap 作为下拉菜单能够 select 特定文件名:

import React, { useState } from 'react';
import { ListGroup, Dropdown } from 'react-bootstrap';
import AWS from 'aws-sdk';

const InputDownload = () => {
  const [template, setTemplate] = useState('Choose Template');

  AWS.config.update({
    accessKeyId: process.env.REACT_APP_ACCESS_ID,
    secretAccessKey: process.env.REACT_APP_ACCESS_KEY,
  });

  const handleDownload = () => {
    const s3 = new AWS.S3();

    const params = {
      Bucket: process.env.REACT_APP_INTERNAL_BUCKET_NAME,
      Key: `templates/${template}`,
    };

    s3.getObject(params, (err, data) => {
      if (err) {
        console.log(err, err.stack);
      } else {
        console.log(data.Body.toString();
      }
    });

}

  return (
    <>
      <form className='bg-white my-4'>
        <Dropdown>
          <Dropdown.Toggle variant='secondary' id='dropdown-basic'>
            {template}
          </Dropdown.Toggle>

          <Dropdown.Menu>
            <Dropdown.Item onSelect={() => setTemplate('T1')}>
              T1</Dropdown.Item>
            <Dropdown.Item onSelect={() => setTemplate('IV1')}>
              IV1
            </Dropdown.Item>
            <Dropdown.Item onSelect={() => setTemplate('IV2')}>
              IV2
            </Dropdown.Item>
            <Dropdown.Item onSelect={() => setTemplate('DV1')}>
              DV1
            </Dropdown.Item>
            
          </Dropdown.Menu>
        </Dropdown>
        <input
          type='submit'
          value='Download'
          className='btn btn-primary btn-block mt-3'
          onClick={handleDownload}
        />
      
      </form>
    </>
  );
};

export default InputDownload;

如有任何帮助,我们将不胜感激!

出于安全考虑,我强烈建议在服务器端执行此操作reasons.Because 在节点环境中执行 React 项目的构建过程后,所有环境变量将直接在客户端上设置为字符串,每个人都可以访问它们(accessKeyId 和 secretAccessKey)。

客户端(浏览器或任何其他环境)无权访问您的服务器环境,这就是您在构建过程中传递这些变量的原因。

另一方面,服务器可以直接访问 运行 的计算机,这就是为什么在服务器端安全地使用环境变量的原因。

对于将来可能想直接从客户端执行此操作的任何其他人,我能够通过使用 Blob 将数据转换为 Blob URL 然后 运行 来实现此目的单击下载时的功能。

我上面的 InputDownload 文件现在看起来像这样:

import React, { useState } from 'react';
import { ListGroup, Dropdown } from 'react-bootstrap';
import AWS from 'aws-sdk';

const InputDownload = () => {
  const [template, setTemplate] = useState('Choose Template');

  AWS.config.update({
    accessKeyId: process.env.REACT_APP_ACCESS_ID,
    secretAccessKey: process.env.REACT_APP_ACCESS_KEY,
  });

const handleClick = (e) => {
    e.preventDefault();
  };

  const handleDownload = () => {
    const s3 = new AWS.S3();

    const params = {
      Bucket: process.env.REACT_APP_INTERNAL_BUCKET_NAME,
      Key: `templates/${template}`,
    };


    function downloadBlob(blob, name = `${template}.csv`) {
      // Convert your blob into a Blob URL (a special url that points to an object in the browser's memory)
      const blobUrl = URL.createObjectURL(blob);
      // Create a link element
      const link = document.createElement('a');
      // Set link's href to point to the Blob URL
      link.href = blobUrl;
      link.download = name;
      // Append link to the body
      document.body.appendChild(link);
      // Dispatch click event on the link
      // This is necessary as link.click() does not work on the latest firefox
      link.dispatchEvent(
        new MouseEvent('click', {
          bubbles: true,
          cancelable: true,
          view: window,
        })
      );

      // Remove link from body
      document.body.removeChild(link);
    }

    s3.getObject(params, (err, data) => {
      if (err) {
        console.log(err, err.stack);
      } else {
        let csvBlob = new Blob([data.Body.toString()], {
          type: 'text/csv;charset=utf-8;',
        });
        downloadBlob(csvBlob, `${template}`);
      }
    });

}

  return (
    <>
      <form className='bg-white my-4' onSubmit={handleClick}>
        <Dropdown>
          <Dropdown.Toggle variant='secondary' id='dropdown-basic'>
            {template}
          </Dropdown.Toggle>

          <Dropdown.Menu>
            <Dropdown.Item onSelect={() => setTemplate('T1')}>
              T1</Dropdown.Item>
            <Dropdown.Item onSelect={() => setTemplate('IV1')}>
              IV1
            </Dropdown.Item>
            <Dropdown.Item onSelect={() => setTemplate('IV2')}>
              IV2
            </Dropdown.Item>
            <Dropdown.Item onSelect={() => setTemplate('DV1')}>
              DV1
            </Dropdown.Item>
            
          </Dropdown.Menu>
        </Dropdown>
        <input
          type='submit'
          value='Download'
          className='btn btn-primary btn-block mt-3'
          onClick={handleDownload}
        />
      
      </form>
    </>
  );
};

export default InputDownload;

你可以那样做,但你可以在 ReactJS 中使用 Axios

new Observable((observer) => {
      var xhr = new XMLHttpRequest();
      xhr.open("get", fileURL, true);
      xhr.responseType = "blob";
      xhr.onload = function () {
        if (xhr.readyState === 4) {
          observer.next(xhr.response);
          observer.complete();
        }
      };
      xhr.send();
    }).subscribe((blob: any) => {
      let link = document.createElement("a");
      link.href = window.URL.createObjectURL(blob);
      link.download = elem.material.driverUrl;
      link.click();
    });