如何在GraphQL和Vue中构建文件处理应用程序
介绍
在本教程中,我们将通过构建一个全栈应用程序来了解如何在 GraphQL 中处理文件上传。 本教程将分为两个主要部分:构建 GraphQL API 和创建前端应用程序。 GraphQL API 将使用 Apollo Server 构建,前端应用程序将使用 Vue.js 和 Vue Apollo 构建。
先决条件
本教程假设您熟悉 GraphQL 和 Vue。 如需更多指导,您可以学习此 GraphQL with Node 教程,以及此 使用 Vue、GraphQL 和 Apollo Client 构建博客的教程。
我们将要建造的
出于本教程的目的,我们将构建一个相册应用程序,用户可以在其中上传和查看他们的照片。 所有照片将直接上传到Cloudinary。 下面是最终应用程序的快速演示:
第 1 步 — 获取 Cloudinary 密钥
在我们深入研究代码之前,让我们确保我们有我们的 Cloudinary 密钥。 如果您还没有他们的帐户,您可以免费注册。 否则,登录到您的仪表板,您将在其中看到您的帐户详细信息以及您的密钥。
第 2 步 — 构建 GraphQL 服务器
现在,让我们开始构建 GraphQL 服务器。 首先,让我们创建我们的项目目录:
$ mkdir graphql-vue-photo-upload && cd graphql-vue-photo-upload $ mkdir server && cd server $ npm init -y
所有 GraphQL API 相关代码都将在 server 目录中。 接下来,让我们为我们的 GraphQL 服务器安装必要的依赖项:
$ npm install graphql apollo-server cloudinary dotenv
除了安装 Apollo Server 及其依赖之外,我们还安装了 Cloudinary Node.js 库和一个用于读取环境变量的包。
安装完成后,我们就可以开始构建 GraphQL 服务器了。 新建一个src目录,在里面新建一个index.js文件,然后在里面添加如下代码:
// server/src/index.js
const { ApolloServer } = require('apollo-server')
require('dotenv').config()
const typeDefs = require('./schema')
const resolvers = require('./resolvers')
const server = new ApolloServer({
typeDefs,
resolvers
})
server.listen().then(({ url }) => console.log(`Server ready at ${url}`))
接下来,我们需要创建 GraphQL 服务器引用的模式和解析器。 我们将从创建模式开始。 在 src 目录中创建一个 schema.js 并将以下代码粘贴到其中:
// server/src/schema.js
const { gql } = require('apollo-server')
const typeDefs = gql`type Photo {
filename: String!
path: String!
}
type Query {
allPhotos: [Photo]
}
type Mutation {
uploadPhoto(photo: Upload!): Photo!
}`
module.exports = typeDefs
在这里,我们定义了一个 Photo 类型,它包含两个字段:照片的文件名和实际照片的路径。 然后我们定义一个查询 allPhotos 来获取所有上传的照片。 最后,我们有一个用于上传照片的突变。 uploadPhoto 突变接受一个参数,即要上传的照片。 参数是标量类型 Upload,我们的 Apollo Server 可以使用它,因为它内置了对文件上传的支持。 突变将返回上传的照片。
接下来要做的是创建解析器。 仍然在 src 目录中,创建一个 resolvers.js 并在其中添加以下代码:
// server/src/resolvers.js
const cloudinary = require('cloudinary').v2
cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.API_KEY,
api_secret: process.env.API_SECRET
})
const photos = []
const resolvers = {
Query: {
allPhotos () {
return photos
}
},
Mutation: {
async uploadPhoto (parent, { photo }) {
const { filename, createReadStream } = await photo
try {
const result = await new Promise((resolve, reject) => {
createReadStream().pipe(
cloudinary.uploader.upload_stream((error, result) => {
if (error) {
reject(error)
}
resolve(result)
})
)
})
const newPhoto = { filename, path: result.secure_url }
photos.push(newPhoto)
return newPhoto
} catch (err) {
console.log(err)
}
}
}
}
module.exports = resolvers
首先,我们拉入 Cloudinary 库并将其配置为我们的凭据,这些凭据是从环境变量中获取的。 然后我们创建一个空数组来保存我们的照片。 接下来,我们为 allPhotos 查询定义解析器,它返回照片数组。
对于 uploadPhoto 突变,Apollo Server 会将所选文件返回为 Promise,其中包含有关该文件的一堆详细信息,例如:createReadStream、[X161X ]、mimetype 和 encoding。 在本教程中,我们将只使用前两个,因此我们从对象中提取它们。 使用 createReadStream,我们将文件直接流式传输到 Cloudinary。 由于它是异步操作,我们将其包装在 Promise 和 await 中。 如果 Promise 已解析,即文件已成功上传到 Cloudinary,我们创建一个新对象,其中包含上传的文件名和文件的 Cloudinary 路径。 然后我们将新对象推送到照片数组,最后返回新对象。
最后,如果将文件上传到 Cloudinary 时出错,我们可以通过控制台记录错误。
在我们结束 GraphQL API 之前,让我们快速添加我们的环境变量。 直接在server目录下创建一个.env文件,在里面添加如下代码:
// server/.env CLOUD_NAME=YOUR_CLOUD_NAME API_KEY=YOUR_API_KEY API_SECRET=YOUR_API_SECRET
请记住用您的实际帐户详细信息替换占位符。
最后,让我们启动服务器:
$ node src/index.js
服务器应该在 [1](http://localhost:4000) 上运行
第 3 步 - 构建前端应用程序
如前所述,前端应用程序将使用 Vue.js 构建,因此让我们使用 Vue CLI 创建一个新的 Vue.js 应用程序:
$ vue create client
出现提示时,按 Enter 键选择默认预设。 启动应用程序并在我们构建它时让它运行:
$ cd client $ yarn serve
该应用程序应该在 http://localhost:8080 上运行
创建 Vue 应用程序后,让我们安装必要的依赖项:
$ 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 Client 的一个包,允许我们发送 GraphQL 多部分请求。 它将代替 apollo-link 使用。
接下来,让我们配置 Vue Apollo 和这些依赖项。 更新 main.js 如下:
// client/src/main.js
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient } from 'apollo-client'
import { createUploadLink } from 'apollo-upload-client'
import Vue from 'vue'
import VueApollo from 'vue-apollo'
import App from './App.vue'
Vue.config.productionTip = false
Vue.use(VueApollo)
const apolloClient = new ApolloClient({
link: createUploadLink({ uri: 'http://localhost:4000' }),
cache: new InMemoryCache()
})
const apolloProvider = new VueApollo({
defaultClient: apolloClient
})
new Vue({
apolloProvider,
render: h => h(App)
}).$mount('#app')
您会注意到这里我们使用 apollo-upload-client 中的 createUploadLink 来创建 ApolloClient 链接,并将我们的 GraphQL API 端点传递给它。
为了给我们的应用添加一些样式,我们将引入 UIKit。 将下面的行添加到 index.html 的 head 部分:
<!-- client/public/index.html --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.1.5/css/uikit.min.css" />
第 4 步 — 获取照片
我们将从用于获取所有照片的 GraphQL 查询开始。 在 client/src 目录中创建一个 graphql 目录,并在其中创建一个 AllPhotos.js 文件并将以下代码粘贴到其中:
// client/src/graphql/AllPhotos.js
import gql from 'graphql-tag'
export default gql`query allPhotos {
allPhotos {
filename
path
}
}`
出于本教程的学习目的,我们将仅使用 App.vue 组件。 所以让我们更新如下:
// client/src/App.vue
<template>
<section class="uk-section">
<div class="uk-container uk-container-small">
<h2>Photo Album</h2>
<div class="uk-grid uk-child-width-1-3@m">
<div class="uk-margin" v-for="(photo, index) in allPhotos" :key="index">
<div class="uk-card uk-card-default">
<div class="uk-card-media-top">
<img :src="photo.path">
</div>
<div class="uk-card-body">{{ photo.filename }}</div>
</div>
</div>
</div>
</div>
</section>
</template>
<script>
import ALL_PHOTOS from "./graphql/AllPhotos";
export default {
name: "app",
apollo: {
allPhotos: ALL_PHOTOS
}
};
</script>
在 apollo 对象中,我们添加 ALL_PHOTOS 查询以获取所有照片并将其保存在 allPhotos 中。 一旦 allPhotos 被来自我们的 GraphQL API 的数据填充,我们通过循环显示照片。
如果我们查看我们的应用程序,我们应该会得到类似下面的内容:
第 5 步 — 上传照片
当然,我们需要先上传一些照片才能看到它们。 现在让我们实现它。 仍然在 graphql 目录中,创建一个 UploadPhoto.js 并将以下代码粘贴到其中:
// client/src/graphql/UploadPhoto.js
import gql from 'graphql-tag'
export default gql`mutation uploadPhoto($photo: Upload!) {
uploadPhoto(photo: $photo) {
filename
path
}
}`
接下来,将下面的代码片段添加到 App.vue 的 template 部分,就在 Photo Album 标题下方:
// client/src/App.vue <div class="uk-margin"> <input type="file" accept="image/*" @change="uploadPhoto"> </div>
在这里,我们有一个只接受图像的文件输入字段。 更改输入字段时会触发 uploadPhoto 方法。
在 script 部分中,添加:
// client/src/App.vue
import UPLOAD_PHOTO from "./graphql/UploadPhoto";
methods: {
async uploadPhoto({ target }) {
await this.$apollo.mutate({
mutation: UPLOAD_PHOTO,
variables: {
photo: target.files[0]
},
update: (store, { data: { uploadPhoto } }) => {
const data = store.readQuery({ query: ALL_PHOTOS });
data.allPhotos.push(uploadPhoto);
store.writeQuery({ query: ALL_PHOTOS, data });
}
});
}
}
我们从输入事件中提取 target,然后调用 mutate 方法,将 UPLOAD_PHOTO 突变以及所需的参数(通过 [ X170X] 对象)。 我们从 target 对象上的 files 获取选定的文件。 执行突变后,我们通过将新上传的照片添加到 allPhotos 数组来更新缓存。
结论
所以在本教程中,我们看到了如何在 GraphQL 中使用服务器端的 Apollo Server 和客户端的 Vue 和 Vue Apollo 来处理文件上传。 虽然我们使用 Cloudinary 来存储我们的照片,但您也可以将其包装为任何其他云存储服务,甚至可以直接保存到您自己的本地文件系统。