You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 14:25:28