React前端的文件上傳能夠經過Apollo Upload Client來實現。javascript
GraphQL使咱們開發者可以在前端應用和REST API之間實現一個強大而靈活的抽象層。做爲一個3個月前才接觸到GraphQL的實習生,我閱讀了許多文章、博客文章和GraphQL社區成員準備的指南,瞭解如何構建GraphQL服務並將其鏈接到React應用。但這些資源都沒有提到使用GraphQL能夠上傳文件。css
文件上傳也能夠這樣說嗎?也許...html
當我受命管理和構建一個新的功能,包括使用GraphQL實現文件上傳時,我意識到雖然這個功能包含在Apollo Server 2.0中,但大多數關於這個功能的資源都被佈置在不一樣的資源庫中,並且指南也遺漏了重要的步驟。前端
我從閱讀官方博客文章開始研究這個功能,並虔誠地按照它的步驟進行研究,但不幸的是,這是我進入神祕錯誤之海的入口,只由於博客文章不完整。我花了近一週的時間,在github倉庫中跳轉GraphQL文件上傳功能,以找出個人代碼有什麼問題。java
所以,我決定收集這篇指南,展現經過使用GraphQL突變實現基本的文件上傳功能GraphQL服務器和React客戶端的必要步驟。另外,談談我對GraphQL這個功能的經驗,以及爲何做爲一個公司咱們決定選擇不一樣的方法。node
在下面的部分中,本指南將帶您經過必要的步驟來建立一個GraphQL服務器,該服務器能夠處理從React應用程序與Apollo Client發送的文件上傳突變。GraphQL服務器要有2種不一樣的功能;將文件保存到文件系統中和將其流進S3 bucket中。react
全部部件的示意圖,更多細節請查看[github repository](https://github.com/epalaz/gra...)。git
在本指南中,咱們的系統將由如下部分組成。
1.帶有Apollo客戶端包的React應用。
2.使用Apollo服務器包的GraphQL服務器。github
咱們的GraphQL服務器將有能力將上傳的文件保存到文件系統中,並將接收到的數據流流進AWS上的S3 Bucket。爲此,咱們將有2個查詢:singleUpload和singleUploadStream。chrome
要使用apollo-server庫建立一個GraphQL服務器,最簡單的方法是按照它的官方指南,但我仍是會在這裏列出所需步驟。
首先建立一個目錄並安裝包。
$ mkdir your_dir_name $ cd your_dir_name $ yarn init --yes $ yarn add apollo-server graphql graphql-upload $ yarn add aws-sdk
這組命令應該安裝包和建立。下一步就是開始定義出類型定義和解析器。
突變的類型定義和填充物查詢
咱們定義了2種不一樣的突變。singleUpload突變用於上傳文件並將其保存到文件系統的一個目錄中,而singleUploadStream則用於流式傳輸到S3 bucket。這些突變採用Upload標量類型,在Apollo Server 2.0中是默認的,因此它的解析是由apollo服務器本身完成的。Apollo服務器將多部分請求形式映射到這個Upload標量,併爲文件生成一個承諾。這兩個突變都會返回文件類型,該類型由文件名、mimetype和編碼字段組成,雖然這並不符合現實生活中的日程安排,但返回文件的正確字段表示文件正確上傳至GraphQL服務器。
SingleUpload和singleUploadStream突變的解析器
在這些文件上傳解析器中,一個關鍵的細節是文件參數返回的承諾。爲了可以讀取文件流,咱們應該等待承諾被解析。在這個例子中,兩個突變解析器使用了不一樣的方法。singleUpload經過使用then()返回承諾,並將解析後的流保存到文件系統中。另外一方面,singleUploadStream使用await等待承諾解析,經過使用文件流將文件上傳到S3。這些解析器的另外一個區別是,在解析器函數裏面使用await須要將其定義爲async,而singleUpload解析器能夠保持同步。
注:舊的例子能夠顯示文件具備filed stream,而不是createReadStream函數。最新的變動請查看graphql-upload repository。變化的PR](https://github.com/jaydenseri...。
這個server.js文件包含了在localhost的4000端口上啓動Apollo服務器並配置AWS JavaScript SDK的代碼。(更多細節請參考文檔)。使用類型定義和突變解析器建立ApolloServer實例應該足以處理文件上傳,以便保存在文件系統中並上傳到S3。若是你須要進一步幫助在AWS上建立S3 Buckets,請參考這個連接。
const { ApolloServer, gql } = require('apollo-server'); const fs = require('fs') const typeDefs = gql` type File { filename: String! mimetype: String! encoding: String! } type Query { _ : Boolean } type Mutation { singleUpload(file: Upload): File!, } `; const resolvers = { Mutation: { singleUpload: (parent, args) => { return args.file.then(file => { const {createReadStream, filename, mimetype} = file const fileStream = createReadStream() fileStream.pipe(fs.createWriteStream(`./${filename}`)) return file; }); }, }, }; const server = new ApolloServer({ typeDefs, resolvers }); server.listen().then(({ url }) => { console.log(`\`🚀 Server ready at ${url}`); }); });
您能夠經過鍵入如下內容測試GraphQL服務器
$ cd your_project_dir $ node server.js
到你的控制檯。這應該啓動服務器的端口4000的localhost。在這以後鍵入localhost:4000到你的瀏覽器,你應該可以看到Playground頁面上,你能夠發送查詢和突變到服務器進行測試,也能夠看到自動生成的文件的基礎上。
Apollo Server遊樂場截圖顯示定義的突變和查詢。
如今,你能夠驗證你的突變和查詢是否被服務器識別,你已經準備好從你的React應用中向服務器發送graphql請求。
儘管graphiql自己的ide不支持,可是可使用altair測試上傳文件和查看結果。
爲了輕鬆地建立咱們的react應用和它的模板代碼,咱們將使用戶create-react-app工具由Facebook開發。
$ yarn create react-app your-app-name。
$ yarn add apollo-client apollo-upload-client react-apollo graphql-tag
這條命令應該建立一個給定名稱的react應用目錄,並自動生成模板代碼,開始開發咱們的應用。你能夠經過輸入如下命令來測試這個命令是否成功
$ 紗線開始
這時應該在localhost:3000端口上啓動開發服務器,並進行實時更新。
由create-react-app建立的應用示例。
若是你能看到這個屏幕,說明你的項目已經設置成功,你能夠開始編輯這個模板來上傳你的文件。
配置Apollo客戶端併爲突變準備Schemas。
import React from 'react'; import logo from './logo.svg'; import './App.css'; import { InMemoryCache } from 'apollo-cache-inmemory' import { createUploadLink } from 'apollo-upload-client' import {ApolloClient} from "apollo-client" import {ApolloProvider, Mutation} from "react-apollo" import gql from "graphql-tag" const apolloCache = new InMemoryCache() const uploadLink = createUploadLink({ uri: 'http://localhost:4000', // Apollo Server is served from port 4000 headers: { "keep-alive": "true" } }) const client = new ApolloClient({ cache: apolloCache, link: uploadLink }) const UPLOAD_FILE = gql` mutation SingleUpload($file: Upload) { singleUpload(file: $file) { filename mimetype encoding } } `; function App() { return ( <div className="App"> <ApolloProvider client={client}> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h2>Save Local</h2> <Mutation mutation={UPLOAD_FILE}> {(singleUpload, { data, loading }) => { console.log(data) return (<form onSubmit={() => {console.log("Submitted")}} encType={'multipart/form-data'}> <input name={'document'} type={'file'} onChange={({target: { files }}) => { const file = files[0] file && singleUpload({ variables: { file: file } }) }}/>{loading && <p>Loading.....</p>}</form>)} } </Mutation> </header> </ApolloProvider> </div> ); } export default App;
這段代碼建立了一個基本的UI,有1個表單,一個是singleUpload,在這個例子中,onChange事件被用來啓動文件上傳突變函數,但你也能夠選擇用你所選擇的事件來啓動它。作文件上傳onChange可讓你在文件被選中後當即上傳。另外一個細節是將表單元素的encType爲multipart/form-data。查看這個連接來閱讀關於GraphQL多部分請求規範的更多細節。在設置了這些組件的渲染函數後,你的UI應該看起來有點相似於這樣。
React應用實例的UI
第三步:測試和上傳文件
**測試文件系統保存
在測試以前,請確保服務器和react應用程序都已啓動並運行。若是沒有,請按照步驟1和2啓動它們。讓咱們嘗試用singleUpload**突變上傳一個文件,看看它是否保存在服務器文件系統中。
若是你檢查控制檯,你應該能夠看到GraphQL服務器返回的上傳文件信息。這意味着咱們的上傳成功了,剩下的事情就是驗證服務器是否將文件保存到文件系統中。
Aaand voila,"no-image.png "文件成功上傳並保存到文件系統。
能夠經過chrome devtools查看具體的提交內容。看到這個內容,再去閱讀graphql文件提交規範,會容易很多。而且,能夠參照此規範,使用Vanillajs來編寫客戶端代碼,而不是非要使用React不可。