用 GraphQL 添加文件上传功能

阅读时长 13 分钟读完

GraphQL 是现代 Web 开发中的一种常用 API 查询语言,它的一个重要特点是通过声明式的方式定义 API 结构,能够帮助前端开发者更好地组织数据并进行一些高度灵活的查询操作。除此之外,由于其设计上的优雅和可扩展性,GraphQL 也被广泛地应用于一些高级特性的实现,例如文件上传等复杂场景。

本文将介绍如何使用 GraphQL 实现文件上传功能,文中将详细讲解使用 GraphQL 做文件上传时需要注意的问题和流程,并给出一个用于 Node.js 的示例代码。

文件上传的实现方式

在传统的 web 开发中,文件上传通常是通过表单提交的方式实现的。当用户选取文件上传时,浏览器会将该文件打包成 multipart/form-data 格式,然后通过表单提交给服务器端。在服务端代码中接收到这个表单 POST 请求时,我们可以从 request 对象中拿到文件数据,并将它们存储在数据库或者文件系统中。

如果我们使用 HTTP GraphQL 服务器来接受这个表单请求,上述的实现方式就失效了。GraphQL 的查询语句是通过 POST 请求体中的 JSON 数据进行传递的,而 multipart/form-data 不是 JSON 的格式。因此,我们需要使用特殊的方式来支持文件上传,包括:

  • 将文件数据打包在 query 变量中,这部分数据使用文件流的方式传输。
  • 定义一个上传文件的 GraphQL mutation ,这个 mutation 的参数包含文件数据和文件元数据等信息。
  • 通过解析请求头和请求体得到上传的文件数据,将这些数据合并到 GraphQL 变量中,最后对 GraphQL mutation 进行调用。

GraphQL 文件上传的实现细节

在前述的方法中,我们提到了将文件数据打包在 query 变量中,这个做法存在一个非常明显的缺点:文件过大时容易导致 GraphQL query string 遭遇大小限制发生错误。

因此,常见的做法是为每个请求生成一个唯一的上传 token,在客户端发送 GraphQL 变量时将上传 token 的地址填写进去,并在服务端得到文件流信息时,再进行解析和上传。另外,GraphQL 文件上传还需要注意以下几个细节:

服务端 multer 中间件

解析含有文件数据的请求时,我们需要使用 multer 中间件,Multer 是一个 Node.js 中间件,用于处理 Multipart/form-data 类型的数据,主要用于上传文件。通过 multer,我们可以轻松的对上传的文件进行处理,并将文件的元数据加工后,传给后续的处理逻辑:

将文件数据流写入磁盘

当文件数据流传输完成后,我们需要将文件数据流写入磁盘。Multer 中间件已经帮我们完成了文件数据流到磁盘的转换,我们只需要关注文件的元数据信息就行了:

-- -------------------- ---- -------
----- -------- - ------------------
----- -------- - -------------------------------------------------
----- ---- - ----------------------

----------------------------------
   ---------------------------------
   ------------- -- -- -
       -- ------------
   ---

生成文件的下载地址

最后,我们需要在服务端生成一个下载地址并返回给客户端。这个地址应该是一个能直接下载文件的链接:

-- -------------------- ---- -------
----- ----------- - --------------------------------------------
----------
    -------- -----
    ----- -
        ------------
        ---------
        ---------
        ----- --------------
    --
---

GraphQL Upload API 的实现

现在让我们看一下如何使用 GraphQL 实现一个文件上传的接口。首先,定义一个 uploadfile mutation:

其中,上传的文件类型指定为 Upload 类型。然后,我们需要在 GraphQL 中实现此 mutation 接口:

-- -------------------- ---- -------
------ --------------- ---- -----------------
------ - ----------------- - ---- -----

----- --------- - -
  ------- --------------
  --------- -
    ----- ------------------ - ---- -- -
        ----- ---------- - ----- -----
        ----- -------- - --------------------
        ----- -------- - ------ -----------------------------------------------------
        ----- --------------- - -------------------------------------------

        ---------------------------------

        ------ -
          ---------
          ---------
          --------- --------
        --
    --
  --
--

在这个 resolver 中,我们做了以下几件事情:

  1. 从参数中获取到上传的文件数据。
  2. 获得文件的 MIME 类型和文件名。
  3. 通过 fs 模块的 createWriteStream 函数将文件数据流写入磁盘。
  4. client 通过 response 拿到文件地址,并保存:这里,我们需要使用 Node.js 的 createReadStream 和 createWriteStream 模块,将文件的数据流从客户端的请求中拿出并写入到磁盘的某个位置。同时,我们需要返回上传成功的信息,例如文件的文件名、文件类型、文件大小等。

用 Node.js 实现文件上传的 GraphQL 例子

提供一个完整的用于 Node.js 的示例代码,以下是 server.js 文件内容:

-- -------------------- ---- -------
----- - ------------- --- - - ---------------------------------
----- ------- - -------------------
----- - ----------------- - - --------------
----- - ------------- - - --------------------------
----- ---- - ----------------
----- ---- - ----------------

----- -------- - ----
  ------ ------

  ---- -------------------- -
    --------- -------
    ----- ----
    --------- -------
    ---- -------
  -

  ---- ----- -
    ------ -------
  -

  ---- -------- -
    ---------------- --------- ---------------------
  -
--

----- --------- - -
  ------ -
    ------ -- -- ------ -------
  --
  --------- -
    ----------- ----- -------- - ---- -- -- -
      ----- - ----------------- --------- -------- - - ----- -----

      ----- ---------- - -------------------
      ----- --------- - -----------
      ----- ----------- - ---------------------------

      ----- ------ - ------------------
        ---------------------------------------------------
      --

      ----- ------------------------

      ------ -
        ---- ---------------------------------------
        ---------
        --------- ------------
        ----- ---------------------
      --
    --
  --
  ------- --------------
--

----- --- - ----------
----------------

----- ------ - --- --------------
  ---------
  ----------
---

------------------------ --- ---

------------------- -------------------------------------- -------------

------------ ----- ---- -- -- --
  ------------
    ------- ----- -- -------------------------------------------
  -
--

在上传完成之后,服务器会将上传的文件存储到 ./uploads 目录下。

客户端代码如下:

-- -------------------- ---- -------
------ - ------------- --------------- --- - ---- -----------------
------ - ---------------- - ---- -----------------------
------ ------ - -------- - ---- --------
------ ---- ---- -------------
------ ------------

----- ---------- - ------------------
  ---- --------------------------------
---

----- ------ - --- --------------
  ------ --- ----------------
  ----- -----------
---

-------- ----- -
  ----- -------------- ---------------- - ---------------
  ----- ---------------- ------------------ - ------------
  ----- --------- ----------- - ---------------

  ----- ------------ - --- -- -
    -------------------
    ----- -------- - --- -----------
    ----------------------- --------------
    ----- -------- - ----
      -------- ------- -------- -
        ---------- ------ ------ -
          ---
          --------
          --------
          ----
        -
      -
    --
    ------
      ---------
        ---------
        ---------- -
          ----- ---------------------
        --
        ----------------- ---------- -- -
          --------------------------------------------- - --------------- - ------
        --
      --
      ---------------- -- -
        -----------------------------------------
      --
      -------------- -- -
        --------------------
      ---
  --

  ----- ------------ - ------- -- -
    ---------------------------------------
  --

  ------ -
    --------------- ----------------
      ---- ----------------
        ------- -----------------------
          ---- ---------- -------------------- ---------- --
          ----- ------------------------
            ------ ----------- ----------- ----------------------- --
            --- --
            ---- --------------- --------------
              --------------- - - -- -
                --
                  ------------------------------
                  ---- -------------- ---------- ------ ---- -------------
                    ----
                      -------------- ---------- ------------
                      -------- ------ -------------------- --
                    -------
                  ------
                ---
              --
              ------- ------------- ---------------------- ---------- ---- ---- ---------
                ------
              ---------
            ------
          -------
          -------- -- -
            ---- -----------------
              -- -------------- --------------- ------------- ------------
                -------- ----
              ----
            ------
          --
        ---------
      ------
    -----------------
  --
-

------ ------- ----

可以我们发现通过 apollo-upload-client 和 FormData 使 ApolloClient 支持文件上传。我们只需要创建 Upload 类型的 mutation,实现需要的逻辑即可。

总结

GraphQL 是一种强大的 API 查询语言,并支持上传文件这样的复杂的场景。通过本文所分享的方式,我们可以让文件上传和数据查询的逻辑更为灵活和高度可控,能够更好地满足实际需求。希望本文对您有所帮助,也欢迎在评论区分享您的思路和实践。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/64586a3b968c7c53b0acbe1e

纠错
反馈