在 MERN 堆栈 Web 应用程序中存储图像的最佳方式

IT技术 node.js reactjs mongodb firebase mern
2021-05-12 05:08:23

我正在使用 MERN 堆栈(MongoDB、Express Server、ReactJS 前端和 NodeJS 后端)构建一个 Web 应用程序,并且想知道一些从后端存储图像的好方法。

过去,我直接从前端使用 Firebase 进行身份验证和存储。当我在 MongoDB 中处理我自己的用户身份验证模型时,是否仍然可以使用 firebase 存储,如果可以,它将来自前端还是后端。如果它来自前端,我如何在没有 Firebase 身份验证的情况下保护它?

我读过的其他选项是使用 GridFS 将图像存储到 MongoDB 或使用 Multer 存储在服务器上。

一旦我想到了解决方案,我将能够阅读文档并弄清楚如何完成它。

任何建议表示赞赏。

4个回答

一种选择是在客户端将图像上传到Cloudinary,并使用您自己的 API 将返回的 URL 保存到 MongoDB。Cloudinary 不仅可以托管您的图像,还可以处理图像处理和优化等。

基本上你要做的是:

  1. 注册一个 Cloudinary 帐户
  2. 转到设置 -> 上传
  3. 添加带有“未签名模式”的“上传预设”以启用未签名上传到 Cloudinary

那么你的上传功能可以是这样的:

async function uploadImage(file) { // file from <input type="file"> 
  const data = new FormData();
  data.append("file", file);
  data.append("upload_preset", NAME_OF_UPLOAD_PRESET);

  const res = await fetch(
    `https://api.cloudinary.com/v1_1/${YOUR_ID}/image/upload`,
    {
      method: "POST",
      body: data,
    }
  );
  const img = await res.json();
  // Post `img.secure_url` to your server and save to MongoDB
}

我认为使用 multer 是非常方便的方法。

您可以使用 multer 将图像上传到文件夹中,并将参考 URL 存储在 MongoDB 中。如果您愿意托管您的 MERN 应用程序,这也很重要。您不需要任何第三方帮助,例如 firebase 或 Cloudinary 上传和身份验证(您已经这样做了)。

因此,您可以使用自己的功能托管自己的应用程序。没有外部成本(仅适用于域:D)

这可能会帮助您获得一个简短的想法。

const InstrumentImageStore = multer.diskStorage({
  destination: function (req, file, callback) {
    const userId = req.userId;
    const dir = `instrumentImgs/${userId}`;
    fs.exists(dir, (exist) => {
      if (!exist) {
        return fs.mkdir(dir, (error) => callback(error, dir));
      }
      return callback(null, dir);
    });

  },
  filename: function (req, file, callback) {
    callback(null, Date.now() + "-" + file.originalname);
  },
});

router.post(
  "/add/instrument",
  [isauth, multer({ storage: InstrumentImageStore }).array("imageArr", 5)],
//isauth is another middleware that restricts requests using JWT
  instrumentController.addInstrument
);

我最终从 Firebase Admin SDK 实现了 Firebase Storage,并使用 Multer 将图像存储在内存中,直到我将它们加载到 Firebase。

https://firebase.google.com/docs/storage/admin/start

const uploader = multer({
  storage: multer.memoryStorage(),
  limits: {
    fileSize: 5 * 1024 * 1024,
  },
});


// @route   POST api/users/upload/avatar
// @desc    Upload a new avatar and save to storage
// @access  Private
router.post('/upload/avatar', [auth, uploader.single('image')], async (req, res, next) => {
  if (!req.file) {
    res.status(400).json({ msg: 'No file submitted.' });
    return;
  }

  try {
    const blob = firebase.bucket.file(req.file.originalname);
    const blobStream = blob.createWriteStream({
      gzip: true,
      resumable: false,
      metadata: {
        contentType: req.file.mimetype,
      },
    });

    blobStream.on('error', (err) => next(err));

    blobStream.on('finish', () => {
      publicUrl = `https://firebasestorage.googleapis.com/v0/b/${
        firebase.bucket.name
      }/o/${encodeURI(blob.name)}?alt=media`;

      res.status(200).json({
        photoURL: publicUrl,
      });

      User.findById(req.user.id).then((user) => {
        user.photoURL = publicUrl;
        user.save();
      });
    });

    blobStream.end(req.file.buffer);
  } catch (error) {
    console.error(error.message);
    res.status(500).send({ msg: 'A Server error occurred' });
  }
});

认为如果将来有人偶然发现这篇文章,这可能会有所帮助。

您可以通过 [实现自定义提供程序](https://firebase.google.com/docs/auth/web/custom-auth为后者将任何外部身份验证系统连接到 Firebase 身份验证

这要求您在受信任的环境中运行代码,例如您控制的服务器或 Cloud Functions,您可以在其中获取用户的身份验证结果并将其转换为 Firebase 身份验证令牌。

然后客户端使用该令牌登录 Firebase,从那一刻起,Storage(和其他服务)就像以前一样了解用户。