如何在GraphQL和Vue中构建文件处理应用

介绍 (Introduction)

In this tutorial, we’ll go over how to handle file uploads in GraphQL by building a full-stack app. This tutorial will be divided into two main sections: building the GraphQL API, and creating the frontend app. The GraphQL API will be built using Apollo Server and the frontend app will be built with Vue.js and Vue Apollo.

在本教程中,我们将介绍如何通过构建全栈应用程序来处理GraphQL中的文件上传。 本教程将分为两个主要部分:构建GraphQL API和创建前端应用程序。 GraphQL API将使用Apollo Server构建,而前端应用程序将使用Vue.js和Vue Apollo构建。

先决条件 (Prerequisites)

This tutorial assumes you are comfortable with GraphQL and Vue. For more guidance, you can learn from this GraphQL with Node tutorial, and this tutorial that builds a blog with Vue, GraphQL, and Apollo Client.

本教程假定您熟悉GraphQL和Vue。 有关更多指导,您可以从此带有Node的GraphQL教程中学习,该教程使用Vue,GraphQL和Apollo Client构建博客

我们将要建设的 (What we’ll be building)

For the purpose of this tutorial, we’ll be building a photo album app, where users will be able to upload as well as view their photos. All the photos will be uploaded directly to Cloudinary. Below is a quick demo of the final app:

就本教程而言,我们将构建一个相册应用程序,用户可以在其中上传和查看他们的照片。 所有照片将直接上传到Cloudinary 。 以下是最终应用程序的快速演示:

第1步-获取Cloudinary密钥 (Step 1 — Getting Cloudinary keys)

Before we dive into code, let’s make sure we have our Cloudinary key in place. If you don’t already have an account with them, you can signup for free. Otherwise, login to your dashboard where you would see your account details along with your keys.

在深入研究代码之前,让我们确保已准备好Cloudinary密钥。 如果您还没有他们的帐户,则可以免费注册 。 否则,登录到仪表板,您将在其中看到您的帐户详细信息以及密钥。

第2步-构建GraphQL服务器 (Step 2 — Building the GraphQL server)

Now, let’s start building the GraphQL server. First, let’s create our project directory:

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

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

All the GraphQL API related code will be inside the server directory. Next, let’s install the necessary dependencies for our GraphQL server:

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

$ npm install graphql apollo-server cloudinary dotenv

Addition to installing Apollo Server and it dependence, we also install the Cloudinary Node.js library and a package to read environment variables.

除了安装Apollo Server及其依赖性外,我们还安装了Cloudinary Node.js库和一个用于读取环境变量的软件包。

Once that’s done installing, we can start building the GraphQL server. Create a new src directory and create a new index.js file inside it, then add the following code in it:

安装完成后,我们可以开始构建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}`))

Next, we need to create the schema and resolvers which our GraphQL server references. We’ll start with creating the schema. Create a schema.js inside src directory and paste the following code into it:

接下来,我们需要创建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

Here, we define a Photo type that comprises of two fields: the filename of the photo and the path to the actual photo. Then we define a single query allPhotos for fetching all uploaded photos. Lastly, we have a mutation for uploading a photo. The uploadPhoto mutation accepts a single argument, which is the photo that is to be uploaded. The argument is of the scalar type Upload, which is made available to us my Apollo Server, since it has built-in support of file upload. The mutation will return the uploaded photo.

在这里,我们定义了一个Photo类型,该类型包括两个字段:照片的文件名和实际照片的路径。 然后,我们定义一个查询allPhotos以获取所有上传的照片。 最后,我们有一个上传照片的变体。 uploadPhoto变异接受一个参数,即要上传的照片。 该参数具有标量类型Upload ,我的Apollo服务器可以使用该参数,因为它具有对文件上传的内置支持。 突变将返回上传的照片。

The next, thing to do is create the resolvers. Still inside src directory, create a resolvers.js and add the code below in it:

接下来要做的是创建解析器。 仍然在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

First, we pull in the Cloudinary library and configured it will our credentials, which are getting from the environment variables. Then we create an empty array, which will hold our photos. Next, we define the resolver for the allPhotos query, which returns the photos array.

首先,我们引入Cloudinary库,并配置它来获取我们的凭据,这些凭据是从环境变量中获取的。 然后我们创建一个空数组,将保存我们的照片。 接下来,我们为allPhotos查询定义解析器,该查询返回photos数组。

For the uploadPhoto mutation, Apollo Server would return the selected file as Promise, which contains a bunch of details about the file, such as: createReadStream, filename, mimetype and encoding. In this tutorial, we’ll only be making use of the first two, so we extract them from the object. Using createReadStream, we stream the file directly to Cloudinary. Since it is an asynchronous operation we wrap it in a Promise and awaits it. If the Promise was resolved, that is, the file was uploaded successfully to Cloudinary, we create a new object containing the uploaded file name and the Cloudinary path to the file. Then we push the new object to the photos array and finally return the new object.

对于uploadPhoto突变,Apollo服务器将返回选定的文件Promise ,其中包含有关该文件的大量详细信息,例如: createReadStreamfilenamemimetypeencoding 。 在本教程中,我们仅使用前两个,因此我们从对象中提取它们。 使用createReadStream ,我们将文件直接流式传输到Cloudinary。 由于它是异步操作,因此将其包装在Promiseawait它。 如果Promise已解决,即文件已成功上传到Cloudinary,我们将创建一个新对象,其中包含上载的文件名和文件的Cloudinary路径。 然后,将新对象推送到photos数组,最后返回新对象。

Lastly, if there was an error uploading the file to Cloudinary, we can console log the error.

最后,如果将文件上传到Cloudinary时出错,我们可以控制台记录该错误。

Before we wrap up the GraphQL API, let’s quickly add our environment variables. Create a .env file directly in the server directory and add the code below in it:

在结束GraphQL API之前,让我们快速添加环境变量。 直接在server目录中创建一个.env文件,并在其中添加以下代码:

// server/.env

CLOUD_NAME=YOUR_CLOUD_NAME
API_KEY=YOUR_API_KEY
API_SECRET=YOUR_API_SECRET

Remember to replace the placeholders with your actual account details.

请记住将占位符替换为您的实际帐户详细信息。

Finally, let’s start the server:

最后,让我们启动服务器:

$ node src/index.js

The server should be running on [http://localhost:4000](http://localhost:4000)

服务器应在[http://localhost:4000](http://localhost:4000)

步骤3 —构建前端应用程序 (Step 3 — Building the Front-end App)

As already mentioned, the frontend app will be built with Vue.js, so let’s create a new Vue.js app using the Vue CLI:

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

$ vue create client

When prompted, press enter to select the default preset. Start the app and leave it running while we build on it:

出现提示时,按Enter键选择默认预设。 启动应用程序,并在我们构建应用程序的同时保持运行状态:

$ cd client
$ yarn serve

The app should be running on http://localhost:8080

该应用程序应在http:// localhost:8080上运行

Once the Vue app is created, let’s install the necessary dependencies:

创建Vue应用后,让我们安装必要的依赖项:

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

Out of these dependencies, the one that is new and that I’d like to point out is [apollo-upload-client](https://github.com/jaydenseric/apollo-upload-client). It’s a package for Apollo Client that allows us to send GraphQL multipart requests. It will be used in place of apollo-link.

在这些依赖关系中,我要指出的是一个新的依赖关系[apollo-upload-client](https://github.com/jaydenseric/apollo-upload-client) 。 这是Apollo Client的软件包,使我们可以发送GraphQL多部分请求。 它将代替apollo-link

Next, let’s configure Vue Apollo and these dependencies. Update main.js as below:

接下来,让我们配置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')

You’ll notice here we are using createUploadLink from apollo-upload-client to create the ApolloClient link, passing to it our GraphQL API endpoint.

你会注意到这里我们使用createUploadLinkapollo-upload-client创建ApolloClient链接,将其传递给我们的GraphQL API端点。

To give our app a bit of styling, we’ll be pulling in UIKit. Add the line below to head section of index.html:

为了给我们的应用程序一些样式,我们将引入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 —提取照片 (Step 4 — Fetch Photos)

We’ll start with the GraphQL query for fetching all our photo. Create a graphql directory inside the client/src directory and with in, create a AllPhotos.js file and paste the code below into it:

我们将从GraphQL查询开始,以获取所有照片。 创建graphql的目录里面client/src的目录并创建一个AllPhotos.js文件并粘贴到下面它的代码:

// client/src/graphql/AllPhotos.js

import gql from 'graphql-tag'

export default gql`query allPhotos {
    allPhotos {
      filename
      path
    }
  }`

For the learning purposes of this tutorial, we’ll be making use of just the App.vue component. So let’s update it as below:

出于学习本教程的目的,我们将仅使用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>

Within the apollo object, we add the ALL_PHOTOS query to fetch all photos and save it in a allPhotos. Once allPhotos is populated with data from our GraphQL API, we display the photos by looping through them.

apollo对象中,我们添加ALL_PHOTOS查询以获取所有照片并将其保存在allPhotos 。 一旦allPhotos会填充我们的GraphQL API的数据,我们通过他们循环显示的照片。

If we view our app, we should get something similar to below:

First view of app with "Photo Album" title

如果我们查看我们的应用程序,我们应该得到类似于以下内容:

第5步-上传照片 (Step 5 — Uploading Photos)

Of course, we need to have uploaded some photos before we can see them. Let’s implement that now. Still inside the graphql directory, create a UploadPhoto.js and paste the code below into it:

当然,我们需要上传一些照片才能看到它们。 让我们现在实现它。 仍然在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
    }
  }`

Next, add the snippet below to the template section of App.vue, just below the Photo Album heading:

接下来,将下面的代码段添加到App.vuetemplate部分中,就在相册标题的下面:

// client/src/App.vue

<div class="uk-margin">
  <input type="file" accept="image/*" @change="uploadPhoto">
</div>

Here, we have a file input field that accepts only images. On change of the input field a uploadPhoto method is triggered.

在这里,我们有一个仅接受图像的文件输入字段。 更改输入字段后,将触发uploadPhoto方法。

In the script section, add:

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 });
      }
    });
  }
}

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

我们从输入事件中提取target ,然后调用mutate方法,将UPLOAD_PHOTO突变以及所需的参数(通过variables对象)传递给它。 我们从选定的文件files上的target对象。 一旦执行了突变,我们就会通过将新上传的照片添加到allPhotos数组来更新缓存。

带“选择文件”按钮的应用程序的屏幕截图

结论 (Conclusion)

So in this tutorial, we have seen how to handle file uploads in GraphQL using Apollo Server on the server side and Vue and Vue Apollo on the client side. Though we used Cloudinary to store our photos, you can also wrap it for any other cloud storage service or you can even save directly to your own local filesystem.

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

翻译自: https://www.digitalocean.com/community/tutorials/how-to-build-a-file-handling-app-in-graphql-and-vue

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值