jaydenseric / graphql-upload

Middleware and a scalar Upload to add support for GraphQL multipart requests (file uploads via queries and mutations) to various Node.js GraphQL servers.
https://npm.im/graphql-upload
MIT License
1.43k stars 132 forks source link

Apollo-Server-Express using graphqlUploadExpress multiple files Question #228

Closed ondrovic closed 3 years ago

ondrovic commented 3 years ago

I am having an issue when trying to upload multiple files using Apollo-Server-Express (2.19.0) and graphqlUploadExpress (11.0.0) if I add multiple files it seems to be missing the file from anything after the 1st object in the array.

Any ideas as to what's causing the other items to fail?

typeDef

module.exports = gql`
    scalar Upload
    extend type Mutation {
        singleFileUpload(file: Upload!): File!
        multipleFileUpload(files: [Upload!]!): [File]   
    }

    type File {
        success: Boolean!
        message: String
        filename: String
        uuid: String
        localPath: String
        serverPath: String
        url: String
        mimetype: String
        encoding: String
    }
`;

server.js

app.use(graphqlUploadExpress({ maxFileSize: 50000000, maxFiles: 20}));
new ApolloServer({
    typeDefs,
    resolvers,
    context: ({ req, res }) => ({
      models,
      user: req.user,
      req,
      res
    }),
    uploads: false});

resolver

const processUpload = async (upload) => {
    const { filename, mimetype, encoding, createReadStream } = await upload.file;
    const { ext } = parse(filename);
    const typeDir = mimeTypeUploadDirectory(mimetype);
    const dateDir = submissionFolderDateStructure();
    const stream = createReadStream();
    const id = uuid();
    const uploadLocation = `${dateDir}/${typeDir}`;
    const baseLocation = `${UPLOAD_PATH}/${uploadLocation}`;
    const fileLocation = `${baseLocation}/${id}${ext}`;
    const localPath = join(__dirname, `src/../${fileLocation}`);
    const serverPath = `${uploadLocation}/${id}${ext}`;
    const url = `${URL}/${serverPath}`;

    mkdirp.sync(join(__dirname, `${baseLocation}`));

    return new Promise((resolve, reject) => {
        stream
            .on('error', (error) => {
                if (error) unlink(localPath);
                reject({
                    success: false,
                    message: error.message
                });
            })
            .pipe(createWriteStream(localPath))
            .on('finish', () => {
                resolve({
                    success: true,
                    message: null,
                    filename,
                    uuid: id,
                    localPath,
                    serverPath,
                    url,
                    mimetype,
                    encoding
                });
            });
    });

}
//TODO: add in auth stuff
const resolver = {
    Mutation: {
        singleFileUpload: async (_, { file }) => {
            return await processUpload(file);
        },
        //BUGFIX: items in the array after the first array are missing the file
        multipleFileUpload: async (_, { files }) => {
            let objArr = (await Promise.all(files)).map(processUpload);
            console.log(objArr);
            return objArr;
        }
    }
}

mutation multiple_files_upload_debug returned_promises Also if I look at the files coming in prior to sending them to my processUpload they look correct files_incoming

jaydenseric commented 3 years ago

The problem is in processUpload; the await upload.file is incorrect. It should just be await upload.

Make sure that your graphqlUploadExpress middleware and the GraphQLUpload scalar are both coming from the same version of graphql-upload, and that it's the latest version. Otherwise the scalar won't get the value it's expecting from the middleware.