Express.js:将上传的图像传递到 S3。

时间:2021-08-12 04:10:56

标签: javascript node.js express amazon-s3 multer

我正在尝试通过 Express 将从 React 应用程序中上传的图像传递到管理的 S3 存储桶。我使用的平台 / 主机创建和管理 S3 存储桶,并生成上传和访问 URL。所有这些都工作得很好(我已经在 Postman 中测试了一个生成的上传 URL,并使用二进制正文中的图像进行测试,结果完美)。

我的问题是通过 Express 传递图像。我正在使用 multer 从表单中获取图像,但我假设 multer 将该图像转换为某种文件对象,而 S3 期望某种类型的 Blob 或流。

在下面的代码中,req.file 中存在图像,我从 S3 获得了 200 响应,没有错误,并且当我访问资产 URL 时,URL 工作,但是图像本身缺失。

const router = Router();
const upload = multer()

router.post('/', upload.single('file'), async (req, res) => {
    console.log(req.file)
    const asset = req.file
    const assetPath = req.headers['asset-path']

    let s3URLs = await getPresignedURLS(assetPath)

    const sendAsset = await fetch(
      s3URLs.urls[0].upload_url, // the s3 upload url
      {
        method: 'PUT',
        headers: {
            "Content-Type": asset.mimetype
        },
        body: asset,
        redirect: 'follow'
      }
    )
  console.log("s3 response", sendAsset)

  res.status(200).json({"url": s3URLs.urls[0].access_url });

});

export default router;

我不确定如何将 multer 给我的转换为 AWS S3 接受的内容。我也可以考虑放弃使用 multer,如果有更简单的方法上传二进制文件到 Express。

1 个答案:

答案 0 :(得分:0)

使用 multiparty 代替 multer,从请求对象中获取文件数据。要将文件上传到 s3 存储桶中,可以使用 aws-sdk

const AWS = require("aws-sdk");
const multiparty = require("multiparty");

/**
 * 帮助方法,接收请求对象并返回一个带有数据的 Promise。
 */
const getDataFromRequest = (req) =>
  new Promise(async(resolve, reject) => {
    const form = new multiparty.Form();
    await form.parse(req, (err, fields, files) => {
      if (err) reject(err);
      const bucketname = fields.bucketname[0];
      const subfoldername = fields.subfoldername[0];
      const file = files["file"][0]; // 从返回的文件对象中获取文件
      if (!file) reject("未在表单数据中找到文件。");
      else resolve({
        file,
        bucketname,
        subfoldername
      });
    });
  });

/**
 * 帮助方法,接收请求对象并返回一个带有 AWS S3 对象详细信息的 Promise。
 */
const uploadFileToS3Bucket = (
  file,
  bucketname,
  subfoldername,
  options = {}
) => {
  const s3 = new AWS.S3();

  // 将文件转换为可上传的缓冲区
  const buffer = readFileSync(file.path);

  var originalname = file.originalFilename;
  var attach_split = originalname.split(".");
  var name = attach_split[0];
  // 生成一个新的随机文件名
  const fileName = name;

  // 你文件的扩展名
  const extension = extname(file.path);

  console.log(`${fileName}${extension}`);

  const params = {
    Bucket: bucketname, //Bucketname
    ACL: "private", //Permission
    Key: join(`${subfoldername}/`, `${fileName}${extension}`), // 要保存在 S3 中的文件名
    Body: buffer, // 文件内容
  };

  // 返回一个 Promise
  return new Promise((resolve, reject) => {
    return s3.upload(params, (err, result) => {
      if (err) reject(err);
      else resolve(result); // 返回成功的 AWS S3 请求值
    });
  });
};

router.post('/', upload.single('file'), async(req, res) => {
  try {
    // 从请求对象中提取文件
    const {
      file,
      bucketname,
      subfoldername
    } = await getDataFromRequest(req);

    // 将文件上传到指定的存储桶中
    const {
      Location,
      ETag,
      Bucket,
      Key
    } = await uploadFileToS3Bucket(
      file,
      bucketname,
      subfoldername
    );

    let response = {};
    res["Location"] = Location;
    response["ETag"] = ETag;
    response["Bucket"] = Bucket;
    response["Key"] = Key;

    res.status(200).json(response);
  } catch (error) {
    throw error;
  }
});

请求正文将包含以下字段的表单数据:

  1. bucketname:
  2. subfoldername:
  3. file:FileData