如何使用 signedURLs 和 ReactJS 将文件保存到 AWS?

How to save files into AWS using signedURLs and ReactJS?

我正在尝试将带有常规文本输入的图像附加到我的表单中,以便提交到我的 MongoDB。

这就是我创建 post 的函数的样子:

  const [postData, setPostData] = useState({
    text: '',
    images: null,
    postedto: auth && auth.user.data._id === userId ? null : userId
  });

  const { text, images, postedto } = postData;

  const handleChange = name => e => {
    setPostData({ ...postData, [name]: e.target.value, images: e.target.files });
  };

  const createPost = async e => {
    e.preventDefault();
    await addPost(postData, setUploadPercentage);
  };

从那里开始我的操作 addPost;在这个函数上我调用了两个 API 路由:

// @route       POST api/v1/posts
// @description Add post
// @access      Private
// @task        DONE
export const addPost = (formData, setUploadPercentage) => async dispatch => {
  try {
    // ATTACH FILES
    let fileKeys = [];

    for(let file of formData.images) {
      const uploadConfig = await axios.get(`${API}/api/v1/uploads/getS3url?type=${file.type}`);
      
      await axios.put(uploadConfig.data.url, file, {
        headers: {
          'Content-Type': file.type
        }
      });

      fileKeys.push(uploadConfig.data.key);
    }

    console.log(fileKeys);

    // INSERT NEW BLOG
    const config = {
      headers: {
        'Content-Type': 'multipart/form-data; application/json'
      },
      onUploadProgress: ProgressEvent => {
        setUploadPercentage(
          parseInt(Math.round(ProgressEvent.loaded * 100) / ProgressEvent.total)
        );
        // Clear percentage
        setTimeout(() => setUploadPercentage(0), 10000);
      }
    };

    formData.images = fileKeys;

    const res = await axios.post(`${API}/api/v1/posts`, formData, config);
    
    dispatch({
      type: ADD_POST,
      payload: res.data
    });

    dispatch(setAlert('Post Created', 'success'));
  } catch (err) {
    const errors = err.response && err.response.data.errors;

    if (errors) {
      errors.forEach(error => dispatch(setAlert(error.msg, 'danger')));
    }

    dispatch({
      type: POST_ERROR,
      payload: { msg: err.response && err.response.statusText, status: err.response && err.response.status }
    });
  }
};

我的 getS3url 函数看起来完全像这样:

exports.uploadFile = asyncHandler(async (req, res, next) => {
  const { type } = req.query;
  const fileExtension = type.substring(type.indexOf('/') + 1);
  const key = `${process.env.WEBSITE_NAME}-${req.user._id}-${
    req.user.email
  }-${Date.now().toString()}.${fileExtension}`;

  const params = {
    Bucket: process.env.AWS_BUCKET_NAME,
    Key: key,
    ContentType: type
  };

  s3.getSignedUrl(`putObject`, params, (err, url) => {
    if (err) {
      return next(
        new ErrorResponse(
          `There was an error with the files being uploaded`,
          500
        )
      );
    }

    return res.status(201).json({ success: true, key: url });
  });
});

我想指出,每个 post 可能有多个图像文件,函数应该 return 为每个文件签名 URL;假设我上传了两个文件,然后我应该检索两个 URLS 以便将它们附加到我的 post.

我确定我管理状态以提交数据的方式没有任何问题,因为它总是 return 我在 console.log(postData) 上使用时所期望的,甚至显示文件。

现在我假设问题出在我的操作上,尤其是 /// INSERT NEW BLOG 注释之前的代码,因为当我 console.log(fileKeys) 什么都没有 returned,甚至 error/undefined/null.....我什么都不是!

我的 uploadFile 在与单个文件一起使用时工作正常....不是真的,因为是的,它 return 是 'supposed' 上传文件的 URL 但是当我进入我的 AWS console/bucket,什么都没有..但那是它自己的 post。

我需要什么帮助?

好吧,我正在尝试使用签名 URL 将 one/multiple 文件上传到我的 AWS,并将它们作为字符串 return 附加到我的 post 中。我的动作文件有问题吗?

谢谢!!

对于我的情况,我一直在遍历图像并生成带符号的 URLs 并返回它们,因为 s3 不支持一次用于多个文件的带符号 URL 选项。

最后我找到了自己的解决方案,这里是:

export const addPost = (formData, images, setUploadPercentage) => async dispatch => {
  try {

    let fileKeys = [];

    for(let i = 0; i < images.length; i++) {
      
      /// STEP 3
      const token = localStorage.getItem("xAuthToken");
      api.defaults.headers.common["Authorization"] = `Bearer ${token}`

      const uploadConfig = await api.get(`/uploads/getS3url?name=${images[i].name}&type=${images[i].type}&size=${images[i].size}`);

      // STEP 1
      delete api.defaults.headers.common['Authorization'];

      await api.put(uploadConfig.data.postURL, images[i], {
        headers: {
          'Content-Type': images[i].type
        }
      });

      fileKeys.push(uploadConfig.data.getURL);
    }

    // INSERT NEW BLOG
    const config = {
      onUploadProgress: ProgressEvent => {
        setUploadPercentage(
          parseInt(Math.round(ProgressEvent.loaded * 100) / ProgressEvent.total)
        );
        setTimeout(() => setUploadPercentage(0), 10000);
      }
    };

    // STEP 2
    const token = localStorage.getItem("xAuthToken");
    api.defaults.headers.common["Authorization"] = `Bearer ${token}`

    const res = await api.post(`/posts`, {...formData, images: fileKeys}, config);
    
    dispatch({
      type: ADD_POST,
      payload: res.data
    });

    dispatch(setAlert('Post Created', 'success'));
  } catch (err) {
    const errors = err.response && err.response.data.errors;

    if (errors) {
      errors.forEach(error => dispatch(setAlert(error.msg, 'danger')));
    }

    dispatch({
      type: POST_ERROR,
      payload: { msg: err.response && err.response.statusText, status: err.response && err.response.status }
    });
  }
};