Apollo Server V4文件上传:前端传媒体至Cloudinary存URL到MongoDB
实现方案:Apollo Server V4 + Cloudinary + MongoDB 媒体上传与存储
1. 依赖安装
先安装核心依赖包:
npm install graphql-upload cloudinary mongoose @apollo/server graphql
2. 配置Apollo Server支持文件上传
Apollo Server V4默认不支持文件上传,需手动集成graphql-upload:
import { ApolloServer } from '@apollo/server'; import { startStandaloneServer } from '@apollo/server/standalone'; import { GraphQLUpload, graphqlUploadExpress } from 'graphql-upload'; import { makeExecutableSchema } from '@graphql-tools/schema'; // GraphQL类型定义 const typeDefs = ` scalar Upload type Media { id: ID! url: String! filename: String! mimeType: String! } type Mutation { uploadMedia(file: Upload!): Media! } `; // 后续解析器代码... // 生成可执行Schema const schema = makeExecutableSchema({ typeDefs, resolvers, }); // 启动服务器并挂载文件上传中间件 const { url } = await startStandaloneServer(new ApolloServer({ schema }), { context: async ({ req, res }) => ({ req, res }), listen: { port: 4000 }, middleware: [graphqlUploadExpress({ maxFileSize: 10 * 1024 * 1024, maxFiles: 1 })], });
3. Cloudinary初始化配置
用你的Cloudinary账号信息初始化SDK:
import { v2 as cloudinary } from 'cloudinary'; cloudinary.config({ cloud_name: '你的Cloud名称', api_key: '你的API密钥', api_secret: '你的API密钥', });
4. MongoDB媒体模型定义
创建模型存储Cloudinary返回的媒体信息:
import mongoose from 'mongoose'; const mediaSchema = new mongoose.Schema({ url: { type: String, required: true }, filename: { type: String, required: true }, mimeType: { type: String, required: true }, publicId: { type: String, required: true }, // Cloudinary资源ID,用于后续删除操作 }); const Media = mongoose.model('Media', mediaSchema); export default Media;
5. 实现上传解析器
处理文件流、上传到Cloudinary并保存数据到MongoDB:
import { promisify } from 'util'; import { pipeline } from 'stream'; const pump = promisify(pipeline); import Media from './models/Media.js'; const resolvers = { Upload: GraphQLUpload, Mutation: { uploadMedia: async (_, { file }) => { const { createReadStream, filename, mimetype } = await file; // 直接通过流上传到Cloudinary const uploadResult = await new Promise((resolve, reject) => { const uploadStream = cloudinary.uploader.upload_stream( { resource_type: 'auto', // 自动识别图片/视频 folder: 'media_storage', // Cloudinary存储文件夹 }, (error, result) => { if (error) reject(error); else resolve(result); } ); pump(createReadStream(), uploadStream).catch(reject); }); // 保存媒体信息到MongoDB const media = new Media({ url: uploadResult.secure_url, filename: filename, mimeType: mimetype, publicId: uploadResult.public_id, }); await media.save(); return { id: media._id.toString(), url: media.url, filename: media.filename, mimeType: media.mimeType, }; }, }, };
6. 前端调用示例(Apollo Client)
import { gql, useMutation } from '@apollo/client'; const UPLOAD_MEDIA = gql` mutation UploadMedia($file: Upload!) { uploadMedia(file: $file) { id url filename } } `; function UploadComponent() { const [uploadMedia] = useMutation(UPLOAD_MEDIA); const handleFileUpload = async (e) => { const file = e.target.files[0]; if (!file) return; try { const result = await uploadMedia({ variables: { file } }); console.log('上传完成', result.data.uploadMedia); } catch (err) { console.error('上传失败', err); } }; return <input type="file" onChange={handleFileUpload} />; }
关键注意事项
- 无需本地存储文件,直接通过流上传到Cloudinary,节省服务器资源
- 可根据需求调整Cloudinary的资源类型、存储文件夹、文件大小限制
- 建议在context中添加用户权限验证,仅允许授权用户上传媒体
- 需处理上传异常,比如Cloudinary服务错误、文件格式不兼容等情况
内容的提问来源于stack exchange,提问作者user17371035
相关产品推荐
相关产品推荐

