文件上传
GQLoom 可通过 GraphQL 的 Upload 标量支持文件上传。
下面给出两种常见接入方式:
- 使用
graphql-upload或graphql-upload-minimal的GraphQLUpload标量 - 使用
graphql-yoga提供的File类型
核心步骤
- 使用
silk函数申明Upload或File标量。 - 在
mutation的input中使用Upload或File标量。
使用 GraphQLUpload
以下代码示例适用于 graphql-upload 或 graphql-upload-minimal。
ts
import { mutation, resolver, silk, weave } from "@gqloom/core"
import { ValibotWeaver } from "@gqloom/valibot"
import { GraphQLNonNull } from "graphql"
import { type FileUpload, GraphQLUpload } from "graphql-upload-minimal"
import { createServer } from "node:http"
import { createWriteStream } from "node:fs"
import { pipeline } from "node:stream/promises"
import * as path from "node:path"
import * as fsPromises from "node:fs/promises"
import { createYoga } from "graphql-yoga"
import * as v from "valibot"
const Upload = silk<Promise<FileUpload>>(new GraphQLNonNull(GraphQLUpload))
const uploadResolver = resolver({
upload: mutation(v.string())
.input({
fileName: v.nullish(v.string()),
file: Upload,
})
.resolve(async ({ fileName, file }) => {
const { filename, createReadStream } = await file
const name = fileName ?? filename
const uploadsDir = path.join(import.meta.dirname, "uploads")
await fsPromises.mkdir(uploadsDir, { recursive: true })
const rs = createReadStream()
const ws = createWriteStream(path.join(uploadsDir, name))
await pipeline(rs, ws)
return `file uploaded: ${name}`
}),
})要点:
Upload使用Promise<FileUpload>,需等待后再读createReadStream。- 还需要在适配器中添加对
Upload的解析,具体查看:
使用 File 类型
以下代码示例适用于 graphql-yoga 的 File 类型。
ts
import { mutation, resolver, silk, weave } from "@gqloom/core"
import { ValibotWeaver } from "@gqloom/valibot"
import { GraphQLNonNull, GraphQLScalarType } from "graphql"
import { createServer } from "node:http"
import * as path from "node:path"
import * as fs from "node:fs/promises"
import { createYoga } from "graphql-yoga"
import * as v from "valibot"
const FileScalar = silk(
new GraphQLNonNull(
new GraphQLScalarType<File, File>({
name: "File",
description: "The `File` scalar type represents a file upload.",
})
)
)
const uploadResolver = resolver({
upload: mutation(v.string())
.input({
fileName: v.nullish(v.string()),
file: FileScalar,
})
.resolve(async ({ fileName, file }) => {
const name = fileName ?? file.name
const uploadsDir = path.join(import.meta.dirname, "uploads")
await fs.mkdir(uploadsDir, { recursive: true })
await fs.writeFile(
path.join(uploadsDir, name),
Buffer.from(await file.arrayBuffer())
)
return `file uploaded: ${name}`
}),
})
const schema = weave(ValibotWeaver, uploadResolver)
const yoga = createYoga({ schema })
createServer(yoga).listen(4000)要点:
- Yoga 提供的
File直接支持arrayBuffer(),适合中小文件或先落盘再处理。 - 同样通过
silk包装为非空标量,按需补充校验与权限控制。