用 GraphQL 添加文件上传功能

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


猜你喜欢

  • Socket.io 和 Vue 结合使用实现即时聊天系统

    在当今的数字时代,即时聊天成为了人们生活中不可或缺的一部分,它能够方便人们随时随地地交流信息。在前端类技术中,Socket.io 和 Vue 结合使用具有极高的可扩展性和可定制性,能够很容易地实现一个...

    2 个月前
  • ECMAScript 2017 中的 Object.getOwnPropertyDescriptors:如何使用

    ECMAScript 2017 中的 Object.getOwnPropertyDescriptors:如何使用 ECMAScript 2017 添加了 Object.getOwnPropertyDe...

    2 个月前
  • 使用 Headless CMS 构建多平台沉浸式阅读体验

    前言 如今,Web 端不再是唯一的数字媒体传播方式。移动应用和互动电子书的普及使得阅读经历越来越多样化和丰富化。在这篇文章中,我们将探讨如何使用 Headless CMS 构建一个多平台的沉浸式阅读体...

    2 个月前
  • 使用 create-react-app 快速构建 React SPA 应用

    前言 React 是一个非常流行的开源 JavaScript 库,主要用于构建用户界面。在 React 中,将界面分解成多个组件,使得代码更容易维护、复用和测试。单页面应用程序(SPA)是一种使用 A...

    2 个月前
  • 解决 Material Design 中 EditText 光标颜色不跟随主题变化的问题

    在 Material Design 主题下,Android EditText 的光标颜色默认是蓝色的。然而,当我们改变主题风格时,光标颜色并不会跟随主题变化,导致与主题不搭配,给用户带来困扰。

    2 个月前
  • CSS Reset 的设计思路与实现方法

    前言 在网页开发的过程中,我们经常遇到样式的不兼容问题。例如,不同浏览器对于某些属性的默认值不同,在不同设备上显示也会有所差异。解决这些问题有多种方法,其中一种就是使用 CSS Reset。

    2 个月前
  • CSS Grid 布局与传统布局的对比

    CSS Grid 布局是一种用于网页布局的新技术,它支持更加灵活和复杂的布局操作,提供了更加优秀的视觉效果,可以极大地提升网页的用户体验。与传统布局相比,CSS Grid 布局具有许多优势。

    2 个月前
  • React Redux 如何处理大数据量的展示

    React Redux 是一个基于 React 框架的状态管理工具,它可以帮助开发者更加方便地管理 React 应用的状态并增强应用的性能。然而,当应用需要处理大量的数据时,就需要一些优化手段来提高性...

    2 个月前
  • 通过 AR 技术实现市区无障碍导览系统

    身为一个前端开发工程师,我们能够想象到如何通过 AR(增强现实)技术来构建市区无障碍导览系统。 无障碍导览在现代社会中已经很普遍,它是为了方便聋哑人士,视觉障碍者以及行动不便的人而存在的。

    2 个月前
  • Babel 编译 react-native 项目时出现”Error: The package @babel/runtime@^7.15.0 does not satisfy its siblings'“怎么办?

    背景 Babel 是一款用于编译 JavaScript 代码的工具,它可以将你写的新版 JavaScript 代码转换成旧版 JavaScript 代码,以支持旧版本的浏览器或 Node.js 等环境...

    2 个月前
  • Webpack Encore 学习笔记

    什么是 Webpack Encore? Webpack Encore是一个Web开发工具,它为您提供了使用先进的前端工具构建网站所需的工作流程和配置。Webpack Encore可以用于JavaScr...

    2 个月前
  • 如何构建自己的 Web 服务器并启动多个 Node.js 进程

    在开发前端项目的过程中,我们经常会需要搭建自己的 Web 服务器来测试和调试我们的应用程序。而 Node.js 提供了强大的库和工具来构建和启动我们自己的 Web 服务器。

    2 个月前
  • ECMAScript 2016: 如何使用函数参数解构?

    ECMAScript 2016: 如何使用函数参数解构? 前言 如果你是一名有经验的 JavaScript 开发者,那么你一定已经听过 ECMAScript 2016(又称 ES7)的函数参数解构特性...

    2 个月前
  • PWA 开发常见错误及其修复方法

    PWA(Progressive Web App)是一种新型的 Web 应用程序开发模式,具有类似于原生应用的体验。PWA 应用程序可以被添加到主屏幕,离线时也可以运行。

    2 个月前
  • RxJS debounceTime 方法在 Angular 应用中的实际应用

    RxJS debounceTime 方法在 Angular 应用中的实际应用 随着前端应用的复杂性越来越高,我们需要使用更高效的代码来解决问题,以提升用户体验和应用性能。

    2 个月前
  • 如何使用 Express.js 实现 GitHub 登录

    GitHub 是全球最大的开源代码托管平台,有数百万的开发者在上面分享代码和协作开发。为了方便开发者登录和授权使用 GitHub,GitHub 提供了 OAuth2.0 授权登录机制,开发者可以使用现...

    2 个月前
  • Sequelize 中的数据操作实践及技巧

    Sequelize 是一个 Node.js 中的 ORM(对象关系映射)框架,它能够方便地与多种数据库进行交互,包括 MySQL、PostgreSQL、SQLite 和 Microsoft SQL S...

    2 个月前
  • Redis 如何解决由于内存碎片导致的内存溢出问题

    Redis 是一个流行的内存数据结构存储系统,被广泛用于缓存、消息队列、会话存储等应用。内存是 Redis 最重要的资源,但长时间运行后,Redis 可能会遭受内存碎片(Memory Fragment...

    2 个月前
  • 如何使用 gulp 和 ESLint 来自动化代码格式化

    前端开发的过程中,一个人写代码生产效率是高的,但是在团队中,要想保持代码的规范性,必须对代码进行格式化。而代码格式化的过程往往需要花费开发者很多时间和精力,因此,我们需要使用自动化工具来降低这种负担。

    2 个月前
  • 通过 Web Components 实现前端集成开发

    在现代的前端开发中,一个项目可能会包含多个模块或组件,而这些模块或组件往往需要实现相似的功能,如表格、弹框、轮播图等。如果每个模块或组件都是独立开发、独立维护的,对于开发效率和代码复用率都是很不利的。

    2 个月前