惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
J
Java Code Geeks
博客园 - 聂微东
Microsoft Azure Blog
Microsoft Azure Blog
量子位
T
Tailwind CSS Blog
Vercel News
Vercel News
I
InfoQ
Stack Overflow Blog
Stack Overflow Blog
U
Unit 42
Engineering at Meta
Engineering at Meta
L
LangChain Blog
大猫的无限游戏
大猫的无限游戏
D
Docker
博客园_首页
P
Proofpoint News Feed
月光博客
月光博客
T
The Blog of Author Tim Ferriss
MyScale Blog
MyScale Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Martin Fowler
Martin Fowler
腾讯CDC
N
Netflix TechBlog - Medium
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

Java不加糖的Blog

假设, AI 把我代替的那一刻真的到来 | szhshp 的第三边境研究所 小傻瓜都能懂的 AstrBot QQ 机器人集成 MCP 功能实战指南 | szhshp 的第三边境研究所 《智人之上: 从石器时代到 AI 时代的信息网络简史》阅读笔记 | szhshp 的第三边境研究所 iPadOS 26 无法设置空间场景图片壁纸的解决方法 | szhshp 的第三边境研究所 《一路云海》(终) | szhshp 的第三边境研究所 《一路云海》(四): 如何不按套路旅行 | szhshp 的第三边境研究所 《一路云海》(三): In Ya Mellow Tone | szhshp 的第三边境研究所 《一路云海》(二): 关西世博参观纪实 | szhshp 的第三边境研究所 《一路云海》(一): 新的征程 | szhshp 的第三边境研究所 2025 大阪世博会 [ 3 天前-先到先得 ] 阶段 场馆预约必中独家攻略 | szhshp 的第三边境研究所 Hackathon 随想 | szhshp 的第三边境研究所 一杯双皮奶 | szhshp 的第三边境研究所 Armbian + CasaOS + NAS 配置指南 | szhshp 的第三边境研究所 Docker 构建镜像报错: error getting credentials - err: exit status 1, out: `` | szhshp 的第三边境研究所 Disqus RIP! 论过高的维护成本如何治疗固执的坏习惯 | szhshp 的第三边境研究所 炸弹猫桌游变体规则 | szhshp 的第三边境研究所 《小岛经济学》阅读笔记 | szhshp 的第三边境研究所 《金钱心理学》阅读笔记 | szhshp 的第三边境研究所 为知笔记 RIP: 迁移剩余的笔记 | szhshp 的第三边境研究所 2025 博客第十年展望 - 再见我的过去 | szhshp 的第三边境研究所 我在独立游戏里面致敬的作品 | szhshp 的第三边境研究所 《How to make thing faster》阅读笔记 | szhshp 的第三边境研究所 《The Art of Clean Code》阅读笔记 | szhshp 的第三边境研究所 《Clean Architecture: A Craftsman Guide to Software Structure and Design》阅读笔记 | szhshp 的第三边境研究所 《How AI Works》阅读笔记 | szhshp 的第三边境研究所 游戏策划废案 - Project Uranus | szhshp 的第三边境研究所 游戏策划废案 - Project X | szhshp 的第三边境研究所 人生第一款独立游戏开发复盘 | szhshp 的第三边境研究所 Trap of Life | szhshp 的第三边境研究所 wireguard折腾记录
GraphQL: File Upload & Troubleshooting | szhshp 的第三边...
2020-10-20 · via Java不加糖的Blog

Meta

目录

GraphQL File Upload

All implementations and extensions are based on graphql-multipart-request-spec

Client

ApolloClient Setup

Client is using apollo-upload-client which implemented graphql-multipart-request-spec

Replace HttpLink with createUploadLink

Those two do the same thing, feel free to replace it!

import {
    ApolloClient,
    InMemoryCache
} from '@apollo/client';
import {
    createUploadLink
} from 'apollo-upload-client';

const client = new ApolloClient(config);

Add Scalar

Upload scalar

Due to different dependencies, this may cause some error, see Troubleshooting below

Add Schema

type Mutation {
    singleUpload(file: Upload!): File!
}

type File {
    filename: String!
    mimetype: String!
    encoding: String!
}

Frontend

Provide an input or use other frontend components to select a file:

<input type="file" onchange={fileUpload}>

Then validate the selected file:

const uploadOnChange = async (files: File[]) => {
    if (files.length === 0) return
    if (files.filter((file) => file.size > 10 * 1024 * 1024).length > 0) {
        /* throw error: file size exceed */
        return
    }
    if (
        files.filter(
            (file) => [ `image/png` , `image/jpeg` ].findIndex(
                (type) => file.type === type
            ) === -1
        ).length > 0
    ) {
        /* throw error: unacceptable file type */
        return
    }

    /* trigger mutation here */
}

Backend

GraphQL port it to assets server

If you are using Javascript, skip the import of graphql-upload

If you are using Typescript, you can use graphql-upload for type check, which implemented graphql-multipart-request-spec

import { FileUpload } from "graphql-upload";

const uploadFile = async (filePromise: {
  file: FileUpload;
}): Promise<boolean> => {
  try {
    const { file }: { file: FileUpload } = await filePromise;
    const fileReadStream = file.createReadStream(); // get the file readstream 

    /* ----------------------------- `*/
    /* Option 1: You can save the file on current server */
    /* const writeStream = fs.createWriteStream('fakepath/output.png') */
    /* Convert stream to file */
    /* readStream.pipe(writeStream) */

    /* ----------------------------- */
    /* Option 2: You can port the file to assets server if you need */
    const formData = new FormData();
    formData.append("attachmentData", fileReadStream, file.filename);

    await http.post( `assetsServer/fileUpload` , formData, {
      headers: {
        ...formData.getHeaders(),
      },
      timeout: 30000,
    });
    return true;
  } catch (error) {
    return false;
  }
};

const resolvers = {
  Query: {
    files: () => {
      // Return the record of files uploaded from your DB or API or filesystem.
    }
  },
  Mutation: {
    uploadFile
  },
};

Troubleshooting

There can be only one type named “Upload”

Possibly you included one lib which ALREADY implemented Upload Type, so you just need to delete scalar Upload

Unknown type “Upload”. Did you mean “Float”?

You forget to add the scalar Upload

scalar Upload always causes error :(

  • If I add it -> Error: There can be only one type named "Upload"
  • If I remove it -> Error: Unknown type "Upload". Did you mean "Float"? Oh you got some tricky dependencies.

Try use other names like:

scalar FileUpload

That may help your issue, GraphQL may regard it as custom scalar.

createReadStream() crashes-RangeError: Maximum call stack size exceeded

RangeError: Maximum call stack size exceeded
        at _openReadFs (internal/fs/streams.js:1:1) 

This is due to outdated dependency of fs-capacitor .

To prevent future compatibility issue, set resolutions in package.json :

"resolutions": {
  "graphql-upload": "11.0.0"
},

Be aware that resolutions property is currently only handled by yarn package manager, not by npm

with npm, you have to preinstall an aditionnal module to force resolutions :

"scripts": {
  "preinstall": "npx npm-force-resolutions",
}

References