Файлы размером более 16 КБ повреждаются при загрузке с использованием apollo и graphql

Думаю, я пробовал все, но ничего не работает. У меня есть приложение React, и я хочу загрузить изображения в папку. Я использую сервер Apollo и Graphql.

Странно то, что если файл меньше 17 кб, то работает!. Но что-то большее, чем это, файл будет поврежден. Я вижу файл изображения в папке, но он 0 КБ, и когда я пытаюсь его открыть, он говорит, что изображение содержит ошибки». У меня нет ограничений на размер элементов.

Резолвер: я пытался использовать другой метод, но у меня возникла та же проблема, поэтому я не верю, что проблема в этом.

const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const { UserInputError } = require('apollo-server');
const path = require('path');
const fs = require('fs');

async uploadFile(_, { file }) {
      const { createReadStream, filename, mimetype, encoding } = await file;
       
      const stream = createReadStream();
      const pathName = path.join(__dirname, "../../public/images/", filename);
      await stream.pipe(fs.createWriteStream(pathName));

      return {
        url: `http://localhost:5000/images/${filename}`,
      }
    }  

Определения типов:

    type File {
    url: String!
    filename: String!
    mimetype: String!
    encoding: String!
  }
    type Mutation {
    uploadFile(file: Upload!): File!
  }

Поставщик Аполлона:

import React from 'react';
import App from './App';
import ApolloClient from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
/* import { createHttpLink } from 'apollo-link-http'; */
import { ApolloProvider } from '@apollo/react-hooks';
import { setContext } from 'apollo-link-context';
import { createUploadLink } from 'apollo-upload-client';


const uploadLink = createUploadLink({
  uri: 'http://localhost:5000/graphql'
});

const authLink = setContext(() => {
  const token = localStorage.getItem('jwtToken');
  return {
    headers: {
      Authorization: token ? `Bearer ${token}` : ''
    }
  };
});

const client = new ApolloClient({
  link: authLink.concat(uploadLink),
  cache: new InMemoryCache(),
  onError: ({ networkError, graphQLErrors }) => {
    console.info( 'graphQLErrors', graphQLErrors)
    console.info( 'networkError', networkError)
  }
});

export default (
  <ApolloProvider client = {client}>
    <App />
  </ApolloProvider>
);

Index.js на сервере:

const { ApolloServer, PubSub } = require('apollo-server-express');
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');

const typeDefs = require('./graphql/typeDefs');
const resolvers = require('./graphql/resolvers');
const { MONGODB } = require('./config.js');

const pubsub = new PubSub();

const PORT = process.env.PORT || 5000;

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req }) => ({ req, pubsub })
});

const app = express();
server.applyMiddleware({ app });

app.use(express.static('public'));
app.use(cors());

mongoose
  .connect(MONGODB, { useNewUrlParser: true })
  .then(() => {
    console.info('MongoDB Connected');
    return app.listen({ port: PORT }, () => {
      console.info(`Server running at http://localhost:5000/ Graphql Playground at http://localhost:5000/graphql `);
    });
  })
  .catch(err => {
    console.error(err)
  })

Я пытался сделать форму ввода по-другому, но у меня возникла та же проблема, поэтому я не думаю, что проблема здесь, но я не знаю.

import React, { useCallback } from 'react';
import { useDropzone } from 'react-dropzone';
import { useMutation, gql } from '@apollo/client';

function UploadForm() {

    const [uploadFile] = useMutation(UPLOAD_FILE_MUTATION, {
        onCompleted: data => console.info(data),
    });

    const onDrop = useCallback(
        ([file]) => {
            uploadFile({ variables: { file } });
        },
        [uploadFile]
    );
    const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });

    return (
        <div {...getRootProps()}>
            <input {...getInputProps()}/>
            {isDragActive ? (
                <p>Drop the files here!</p>
            ) : (
                <p>Drag and drop files here, or click to select files</p>
            )}
        </div>
    )
}

const UPLOAD_FILE_MUTATION = gql`
    mutation uploadFile($file: Upload!){
        uploadFile(file: $file){
            url
        }
    }
`

export default UploadForm;

Я подозреваю, что проблема в index.js или провайдере Apollo, но я действительно не знаю. Спасибо.

async/await работает только с промисами, а не с потоками... используйте поиск

xadm 09.12.2020 19:11

Вау, это работает! Большое спасибо

Casper 09.12.2020 21:06
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
2
916
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

«async/await работает только с промисами, а не с потоками» — xadm

Решение:

Изменил резольвер на это:

const path = require('path');
const fs = require('fs');

const Image = require('../../models/Image');

const files = [];
  
  module.exports = {

    Mutation: {
    async uploadProfilePic(_, { file }, context) {
      const { createReadStream, filename, mimetype, encoding } = await file;

      const { ext } = path.parse(filename);
      const randomName = generateRandomString(12) + ext;

        files.push(randomName);

        console.info(file);

        const storeUpload = async ({ stream, filename, mimetype, encoding }) => {
        
            const path = `public/images/${randomName}`
        
            return new Promise((resolve, reject) =>
            stream
                .pipe(fs.createWriteStream(path))
                .on("finish", () => resolve({ 
                category: "profilePic",
                url: `http://localhost:5000/images/${randomName}`, 
                path, 
                filename: randomName, 
                mimetype,
                encoding,
                createdAt: new Date().toISOString(),
                commentsCount: 0,
                likesCount: 0,
                sharesCount: 0
                }))
                .on("error", reject)
            );
        };

        const processUpload = async (upload) => {
          const { createReadStream, filename, mimetype, encoding } = await upload;
          const stream = createReadStream();
          const file = await storeUpload({ stream, filename, mimetype, encoding });
          return file;
        };

        const upload = await processUpload(file);

        
        return {
          url: `http://localhost:5000/images/${randomName}`,
        };
      
    }
  }
};

Можете ли вы опубликовать изменения в коде, которые вы сделали после этого?

Jim 12.12.2020 17:58

Эй, отредактировал пост, обновление внизу.

Casper 12.12.2020 21:28

Другие вопросы по теме