node.js - 从 Postgres - Node/React 返回图像 url 到前端

标签 node.js reactjs express redux

我的应用程序中有一个我正在使用的功能,它允许我提交一个包含图像的表单,然后使用 Multer 将图像存储在一个文件中,并将一个 URL 上传到我的 Postgres 数据库。当我将文件返回到前端时,我只剩下与图像相关的文件名,但我不知道如何添加文件路径以便显示图像。

插入数据库时​​是否应该添加前缀文件路径?或者如果我在前端显示完整的文件路径是否存在安全问题。显然我知道我的前后端应该解耦并独立运行。我也可以在后端之外为图像创建一个单独的文件,但我不确定这是否是推荐的过程。如果这是在专业环境中,它只会由 Google Cloud、AWS 等处理,所以我不确定在这种情况下是否完全可以遵循解耦过程。

我在一些类似的案例中看到过 process-cwd,但我不确定这是否是我需要做的。我可以在前端React组件中硬编码文件路径,然后使用最后有文件名的redux数据吗?

我的照片文件名存储在我的 Redux 商店中,但我不知道从那里去。该图像当时在我的后端/ Assets 文件中。

enter image description here

将图像插入 Postgres 的 API。

exports.createDiveSpot = async (req, res) => {

    const fileNameWithExtension = `${req.file.filename}-${req.file.originalname}`
    const newPath = `./assets/diveSpot/${fileNameWithExtension}`

    fs.rename(req.file.path, newPath, function (err) {
            if (err) {
                console.log(err)
                res.send(500)
            }
            console.log(req.body)
            diveSpot.create({
                diveLocation: req.body.diveLocation,
                diveRegionID: req.body.diveRegionID,
                diveSpotTypeID: req.body.diveSpotTypeID,
                diveSpotDescription: req.body.diveSpotDescription,
                photos: fileNameWithExtension,
            }).then((data) => {
                res.send(data)
            })

更新

在下面的方法中。我注意到我可以在“const fileNameWithExtension”行中轻松输入文件名。我可以在前端输入公共(public)文件的 url 还是应该在后端使用 express 公开文件。我是否应该将后端的文件再次放入前端,因为从技术上讲它已经从前端提交,然后 multer 会将其移至前端?

我在下面包含了我最新的错误消息。

exports.createArticle = async (req, res) => {
    console.log(req.body)
    const fileNameWithExtension = "../home/backend/assets/article/"`${req.file.filename}-${req.file.originalname}`
    const newPath = `./assets/article/${fileNameWithExtension}`
    console.log(req.body)
    fs.rename(req.file.path, newPath, function (err) {
            if (err) {
                console.log(err)
                res.send(500)
            }
            article.create({
                articleTitle: req.body.articleTitle,
                articleContent: req.body.articleContent,
                userID: req.body.userID,
                articleTypeID: req.body.articleTypeID,
                photos: fileNameWithExtension,
            }).then((data) => {
                res.send(data)
            })
                .catch((err) => {
                    res.status(500).send({
                        message: err.message || 'Some error occurred while creating the post.',
                    })
                })
        }
    )}

错误信息

[Error: ENOENT: no such file or directory, rename 'C:\Users\James Greene\WebstormProjects\softwaredevproject\SustainableScuba\backend\assets\article\678c78
193bb73ab287bbb6b644a0c86e' -> 'C:\Users\James Greene\WebstormProjects\softwaredevproject\WebApp\sustainable-scuba-web-app\public\article\678c78193bb73ab28
7bbb6b644a0c86e-sharkfeat.jpg'] {
  errno: -4058,
  code: 'ENOENT',
  syscall: 'rename',
  path: '...\\SustainableScuba\\backend\\assets\\article\\678c78193bb73ab287bbb6b644a0c86e',
  dest: '...\\sustainable-scuba-web-app\\public\\article\\678c78193bb73ab287bbb6b644a0c86
e-sharkfeat.jpg'
}
express deprecated res.send(status): Use res.sendStatus(status) instead controllers\article.controller.js:15:21
Executing (default): INSERT INTO "articles" ("articleID","articleTitle","articleContent","photos","userID","articleTypeID") VALUES (DEFAULT,$1,$2,$3,$4,$5)
 RETURNING *;
Unhandled rejection Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:485:11)
    at ServerResponse.header (...\backend\node_modules\express\lib\response.js:771:1
0)

它在 Redux 的前端呈现并在商店中。我正在尝试在下面的卡片组件上显示图像。

         <IconButton aria-label="settings">
                        <MoreVertIcon />
                    </IconButton>
                }
                title={articleList.articleTitle}
                subheader={userID}/>
            <CardMedia
                id="Photos"
                className={classes.media}
                image={articleList.photos}
                title="Dive Photos"/>
            <CardContent>
                <Typography className="Type" id="Type" variant="body2" color="textSecondary" component="p">
                    {articleType}

最佳答案

包含后端的完整路径是一种糟糕的安全做法,因为它允许攻击者遍历您的安装并找到漏洞。最好将复杂的应用程序拆分为不同的更简单的职责,以便更容易解决。

这个解决方案应该“分离”后端和前端的关注点,以及上传和下载文件的两个用例:

用例 1:将图像文件上传到服务器。

  • 用户从客户端系统中选择图片文件
  • 客户端读取文件并上传到服务器
  • 服务器保存文件并将其关联到内容。 (这是示例代码)

用例 2:下载并在 UI 上显示匹配文章的图像文件。

  • 客户请求一篇文章
  • 服务器发送图片文件的文章内容和ID
  • 客户端请求图像文件并将响应分配给图像组件。
  • 服务器通过 ID 检索文件并将其返回给客户端。
  • 客户端图像组件在客户端系统上呈现图像文件。

因此,您的后端需要以某种方式保存文件,以便它可以发布它供以后使用。评论中提到的最佳做法是将文件放在公共(public)文件夹中并使用名称作为 ID,然后让网络服务器将其视为静态文件。这样可以充分发挥网络和云技术的优势,帮助您加快和维护您的解决方案。

当客户端上传图片时,服务器应将其保存到公共(public)文件夹并返回其名称/ID。上传文件的客户端路径不够,因为服务器无法访问客户端系统。相反,客户端需要在 HTTP 请求中发送带有元数据的字节流。如果您只在上传中设置文件名,则需要像这样读取字节并将其发送到服务器:

JSON 示例:

  // read in the file bytes when opened:
        onFileOpened(e) {
          let data={};
          let reader = new FileReader();
                reader.onloadend = () => { 
                  data.image= reader.result ;
                };
           reader.readAsDataURL(event.target.files[0]);
           data.id=generateId(); 
        
       }
        // send the bytes on submit:
        onSubmit(data){
           POST(data);
        
          }

多部分示例:

使用 multer,客户端应该发送一个 Multipart/Form-Data 请求。这是回退到旧的 HTML 技术并像上面一样对字节进行编码。但是,它仍然是包装器中的字节。

<form action="/profile" method="post" enctype="multipart/form-data">
  <input type="file" name="avatar" />
</form>

然后,当服务器接收到流并调用 createArticle 方法时,它应该读取字节并将它们保存到共享路径、数据库或内容分发网络 (CDN)。服务器应托管或启用端点以从记录的 ID 中获取相同的文件。然后内容可以通过将文件的 ID/名称附加到图像端点来引用该文件。

不是将文件从客户端路径重命名为目标,而是需要将字节写入磁盘:

   fs.createReadStream(req.file).pipe(fs.createWriteStream(newPath ));

这允许将图像托管在多个提供商上,允许您的网络服务器的多个实例并行运行,并允许您独立调整上传和下载服务的大小。

关于node.js - 从 Postgres - Node/React 返回图像 url 到前端,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67014950/

相关文章:

javascript - 如何在我的 TreeItem Material UI 4 中添加图标

javascript - Axios Post 请求在 React JS 中不起作用

javascript - 代码会随着时间降低浏览器性能

javascript - Mongoose 预保存使用不正确的 "this"上下文?

javascript - react / react Hook : child component is not re-rendering after the state is changed?

node.js - 与快速路由器和传递实例的路由分离

node.js - 将文件上传到 Azure Blob 存储时遇到困难

java - Node 服务器拦截从 Angular (http)到java spring后端的所有请求

javascript - Nodejs v5 如何让我们使用 ES6 模块?

node.js - PhantomJS 默认存储任何持久文件吗?