如何使用 GraphQL 和 Vue 构建文件处理应用程序

介绍

本教程将分为两个主要部分:构建 GraphQL API 和创建前端应用程序 GraphQL API 将使用 Apollo Server 构建,前端应用程序将使用 Vue.js 和 Vue Apollo。

前提条件

对于更多指导,你可以从这个学习(https://scotch.io/tutorials/super-simple-graphql-with-node),和这个 教程,构建一个博客与Vue, GraphQL,和阿波罗客户端.

我们将建立什么

对于本教程的目的,我们将建立一个照片专辑应用程序,用户将能够上传以及查看他们的照片. 所有照片将直接上传到 Cloudinary. 下面是最终应用程序的快速演示:

Animated Photo Album App Demo

第1步:获取Cloudinary密钥

在我们沉浸在代码,让我们确保我们有我们的Cloudinary密钥的地方. 如果你还没有帐户与他们,你可以 注册免费.否则,登录到你的仪表板,你会看到你的帐户细节和你的密钥。

Cloudinary dashboard

第2步:构建 GraphQL 服务器

现在,让我们开始构建 GraphQL 服务器,首先,让我们创建我们的项目目录:

1$ mkdir graphql-vue-photo-upload && cd graphql-vue-photo-upload
2$ mkdir server && cd server
3$ npm init -y

所有与 GraphQL API 相关的代码都将在服务器目录中。接下来,让我们为我们的 GraphQL 服务器安装必要的依赖:

1$ npm install graphql apollo-server cloudinary dotenv

除了安装 Apollo Server 及其依赖之外,我们还安装了 Cloudinary Node.js 库和读取环境变量的包。

一旦安装完成,我们可以开始构建 GraphQL 服务器. 创建一个新的'src' 目录,并在其内部创建一个新的 'index.js' 文件,然后添加以下代码:

 1// server/src/index.js
 2
 3const { ApolloServer } = require('apollo-server')
 4require('dotenv').config()
 5const typeDefs = require('./schema')
 6const resolvers = require('./resolvers')
 7
 8const server = new ApolloServer({
 9  typeDefs,
10  resolvers
11})
12
13server.listen().then(({ url }) => console.log(`Server ready at ${url}`))

接下来,我们需要创建我们的 GraphQL 服务器引用的方案和解决方案,我们将开始创建该方案,在src目录中创建一个schema.js,并粘贴以下代码:

 1// server/src/schema.js
 2
 3const { gql } = require('apollo-server')
 4
 5const typeDefs = gql`type Photo {
 6    filename: String!
 7    path: String!
 8  }
 9
10  type Query {
11    allPhotos: [Photo]
12  }
13
14  type Mutation {
15    uploadPhoto(photo: Upload!): Photo!
16  }`
17
18module.exports = typeDefs

在这里,我们定义了由两个字段组成的 " 相片 " 类型:相片的文件名和实际相片的路径。 然后,我们定义一个所有图片的单一查询,以获取所有上传的照片。 最后,我们有一个变种 上传一张照片。 `uploadPhoto'突变接受单一的论据,这就是要上传的照片。 其论据是 " Upload " 的scalar类型,它提供给我们我的阿波罗服务器,因为它内在支持文件上传。 变异会还原上传的照片.

下一步,要做的是创建解析器. 仍然在src目录中,创建一个resolvers.js并添加下面的代码:

 1// server/src/resolvers.js
 2
 3const cloudinary = require('cloudinary').v2
 4
 5cloudinary.config({
 6  cloud_name: process.env.CLOUD_NAME,
 7  api_key: process.env.API_KEY,
 8  api_secret: process.env.API_SECRET
 9})
10
11const photos = []
12
13const resolvers = {
14  Query: {
15    allPhotos () {
16      return photos
17    }
18  },
19  Mutation: {
20    async uploadPhoto (parent, { photo }) {
21      const { filename, createReadStream } = await photo
22
23      try {
24        const result = await new Promise((resolve, reject) => {
25          createReadStream().pipe(
26            cloudinary.uploader.upload_stream((error, result) => {
27              if (error) {
28                reject(error)
29              }
30
31              resolve(result)
32            })
33          )
34        })
35
36        const newPhoto = { filename, path: result.secure_url }
37
38        photos.push(newPhoto)
39
40        return newPhoto
41      } catch (err) {
42        console.log(err)
43      }
44    }
45  }
46}
47
48module.exports = resolvers

首先,我们拖入Cloudinary库并将其配置为我们的身份证,这些身份证来自环境变量,然后我们创建一个空的数组,其中将包含我们的照片,接下来,我们定义了allPhotos查询的解析器,返回了照片数组。

对于 " 上载Photo " 突变,阿波罗服务器将把所选文件作为 " 保证 " 退回,其中载有关于该文件的一连串细节,例如:read Stream'、filename'、mimetype'和编码 ' 。 在此教程中, 我们只使用前两款, 我们使用readStream',将文件直接流到Cloudinary。 由于这是一项同步行动,我们把它包装成承诺'并等待'。 如果保证'问题得到解决,即文件被成功上传到Cloudinary,我们创建一个包含上传文件名和文件的Cloudinary路径的新对象。 然后把新对象推到相片阵列上,最后还原出新对象.

最后,如果将文件上传到 Cloudinary 时出现错误,我们可以将错误记录在 console 上。

在我们包装 GraphQL API 之前,让我们快速添加我们的环境变量,直接在服务器目录中创建一个 `.env’ 文件,并添加以下代码:

1// server/.env
2
3CLOUD_NAME=YOUR_CLOUD_NAME
4API_KEY=YOUR_API_KEY
5API_SECRET=YOUR_API_SECRET

请记住,用您的实际帐户详细信息来替换座位持有人。

最后,让我们开始服务器:

1$ node src/index.js

服务器应该在[http://localhost:4000](http://localhost:4000)上运行。

步骤3 – 构建前端应用程序

如前所述,前端应用程序将使用 Vue.js 构建,所以让我们使用 Vue CLI 创建一个新的 Vue.js 应用程序:

1$ vue create client

当被提示时,点击 Enter 以选择默认预设. 启动应用程序并让它运行,而我们正在建立它:

1$ cd client
2$ yarn serve

應用程式應在 http://localhost:8080上運行。

一旦创建了Vue应用程序,让我们安装必要的依赖:

1$ npm install vue-apollo graphql-tag graphql apollo-cache-inmemory apollo-client apollo-upload-client

在这些依赖中,一个是新的,我想指出的是[apollo-upload-client](https://github.com/jaydenseric/apollo-upload-client)。这是一个为Apollo客户端的包,允许我们发送GraphQL多部分请求。它将被用来代替apollo-link

接下来,让我们配置Vue Apollo和这些依赖。更新main.js如下:

 1// client/src/main.js
 2
 3import { InMemoryCache } from 'apollo-cache-inmemory'
 4import { ApolloClient } from 'apollo-client'
 5import { createUploadLink } from 'apollo-upload-client'
 6import Vue from 'vue'
 7import VueApollo from 'vue-apollo'
 8import App from './App.vue'
 9
10Vue.config.productionTip = false
11
12Vue.use(VueApollo)
13
14const apolloClient = new ApolloClient({
15  link: createUploadLink({ uri: 'http://localhost:4000' }),
16  cache: new InMemoryCache()
17})
18
19const apolloProvider = new VueApollo({
20  defaultClient: apolloClient
21})
22
23new Vue({
24  apolloProvider,
25  render: h => h(App)
26}).$mount('#app')

在这里,你会注意到我们正在使用apollo-upload-client中的createUploadLink来创建ApolloClient链接,将我们的GraphQL API终端传递给它。

为了给我们的应用程序一点风格,我们将在UIKit中拉动,然后将下面的行添加到index.html头部部分:

1<!-- client/public/index.html -->
2
3<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.1.5/css/uikit.min.css" />

第4步:拍摄照片

创建一个graphql目录在client/src目录中,并在里面,创建一个AllPhotos.js文件并粘贴下面的代码:

 1// client/src/graphql/AllPhotos.js
 2
 3import gql from 'graphql-tag'
 4
 5export default gql`query allPhotos {
 6    allPhotos {
 7      filename
 8      path
 9    }
10  }`

对于本教程的学习目的,我们将只使用App.vue组件,所以让我们如下更新:

 1// client/src/App.vue
 2
 3<template>
 4  <section class="uk-section">
 5    <div class="uk-container uk-container-small">
 6      <h2>Photo Album</h2>
 7
 8      <div class="uk-grid uk-child-width-1-3@m">
 9        <div class="uk-margin" v-for="(photo, index) in allPhotos" :key="index">
10          <div class="uk-card uk-card-default">
11            <div class="uk-card-media-top">
12              <img :src="photo.path">
13            </div>
14            <div class="uk-card-body">{{ photo.filename }}</div>
15          </div>
16        </div>
17      </div>
18    </div>
19  </section>
20</template>
21
22<script>
23import ALL_PHOTOS from "./graphql/AllPhotos";
24
25export default {
26  name: "app",
27  apollo: {
28    allPhotos: ALL_PHOTOS
29  }
30};
31</script>

apollo对象中,我们添加了ALL_PHOTOS查询以获取所有照片并将其保存到allPhotos中。

If we view our app, we should get something similar to below: First view of app with "Photo Album" title

步骤5:上传照片

当然,我们需要上传一些照片才能看到它们。让我们现在实现这一点. 仍然在graphql目录中,创建一个UploadPhoto.js并粘贴下面的代码:

 1// client/src/graphql/UploadPhoto.js
 2
 3import gql from 'graphql-tag'
 4
 5export default gql`mutation uploadPhoto($photo: Upload!) {
 6    uploadPhoto(photo: $photo) {
 7      filename
 8      path
 9    }
10  }`

接下来,将下面的片段添加到App.vue模板部分,就在 Photo Album 标题下面:

1// client/src/App.vue
2
3<div class="uk-margin">
4  <input type="file" accept="image/*" @change="uploadPhoto">
5</div>

在这里,我们有一个只接受图像的文件输入字段。在更改输入字段时,触发了上传照片方法。

脚本部分中,添加:

 1// client/src/App.vue
 2
 3import UPLOAD_PHOTO from "./graphql/UploadPhoto";
 4
 5methods: {
 6  async uploadPhoto({ target }) {
 7    await this.$apollo.mutate({
 8      mutation: UPLOAD_PHOTO,
 9      variables: {
10        photo: target.files[0]
11      },
12      update: (store, { data: { uploadPhoto } }) => {
13        const data = store.readQuery({ query: ALL_PHOTOS });
14
15        data.allPhotos.push(uploadPhoto);
16
17        store.writeQuery({ query: ALL_PHOTOS, data });
18      }
19    });
20  }
21}

We get extract the target from the input event, then call the mutate method, passing to it the UPLOAD_PHOTO mutation as well as the required argument (through the variables object). We get the selected file from the files on the target object. Once the mutation is executed, we update the cache by adding the newly uploaded photo to the allPhotos array. Screenshot of App with Choose File Button Screenshot of APp with Images Uploaded

结论

所以在本教程中,我们已经看到如何在GraphQL中使用服务器侧的Apollo Server和客户端的Vue和Vue Apollo来处理文件上传,尽管我们使用Cloudinary来存储我们的照片,但您也可以将其包装到任何其他云存储服务中,甚至可以直接保存到您的本地文件系统中。

Published At
Categories with 技术
comments powered by Disqus