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

Posted

技术标签:

【中文标题】在 MERN 堆栈 Web 应用程序中存储图像的最佳方式【英文标题】:Best way to store images in MERN stack web application 【发布时间】:2020-10-02 20:37:58 【问题描述】:

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

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

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

一旦我想到了一个解决方案,我就可以阅读文档并弄清楚如何完成它。

感谢任何建议。

【问题讨论】:

【参考方案1】:

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

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

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

【讨论】:

【参考方案2】:

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

基本上你要做的是:

    注册 Cloudinary 帐户 进入设置 -> 上传 使用“未签名模式”添加“上传预设”以启用未签名上传到 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

【讨论】:

嘿@Hangindev,这正是我想做的,但我仍然是超级新的 MERN 和 Cloudinary。你介意帮我解决这个问题吗?我只是不能 100% 确定一切都应该去哪里jsfiddle.net/qfme5h8k 嗨@FrazerFindlater,你可以看看我整理的这个codesandbox。【参考方案3】:

我认为使用 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
);

【讨论】:

【参考方案4】:

我最终从 Firebase Admin SDK 实现了 Firebase 存储,并使用 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' );
  
);

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

【讨论】:

以上是关于在 MERN 堆栈 Web 应用程序中存储图像的最佳方式的主要内容,如果未能解决你的问题,请参考以下文章

当同时使用 GraphQL 时,Nodejs 和 Express 在 MERN 堆栈 Web 应用程序中的作用是啥?

即使设置了 sameSite:'none' 和 secure: true 对于 MERN 堆栈 Web 应用程序,Cookie 也不会保存在 chrome 中

如何使用 MERN 堆栈构建动态单页 Web 应用程序?

由于缺少引号,布尔值未在 MERN 堆栈中更新

如何在 MERN 堆栈中发送响应模式?

需要在 MERN 堆栈应用程序中添加视频会议(特别是课堂/教育)的建议