Я пытаюсь отобразить изображения, которые я загрузил в свой серверный сервер с помощью multer

#mysql #node.js #reactjs #multer

#mysql #node.js #reactjs #multer

Вопрос:

Я использую multer для загрузки изображений из моего клиента react. Все изображения сохраняются, они просто не отображаются. Изображения сохраняются, но не отображаются в react. Они установлены в состояние, когда они просто не отображаются. Я вижу их в папке images на серверной части, поэтому я знаю, что multer работает.

Это мой компонент react

 class AddNewDish extends Component {
    constructor(props){
        super(props)
        this.state = {
            name: '',
            imageUrl: '',
            description: ''

        }
    }



    createDishHandler = (event) => {
        event.preventDefault()
        const fd = new FormData();
        fd.append('name', this.state.name)
        fd.append('imageUrl', this.state.imageUrl, this.state.imageUrl.name)
        fd.append('description', this.state.description)
        axios.post('http://localhost:8080/add-new-dish', fd,)
            .then(res => {
                console.log(res, 'res')
            })
        this.props.history.push('/')
    }


    onChange = (e) => {
        switch(e.target.name) {

            case 'imageUrl':
                this.setState({imageUrl: e.target.files[0]});
                break;
            default:
                this.setState({[e.target.name]: e.target.value})
        }
        console.log(e)
    }



    render () {
        console.log(this.state.imageUrl, 'in imgUrl')
        return (
            <div>
             <form onSubmit={this.createDishHandler}> 
                <input
                    label='Name'
                    onChange={this.onChange}
                    name='name'
                    type='text'
                    placeholder='Enter Dish Name'
                    value={this.state.name}
                />

                < input
                    label='Dish Image'
                    onChange={this.onChange}
                    name='imageUrl'
                    type='file'
                    placeholder='Enter Dish Image'
                    // value={this.state.imageUrl}
                />

                 <input
                    label='Description'
                    onChange={this.onChange}
                    name='description'
                    placeholder='Enter Dish Description'
                    type='text'
                    value={this.state.description}
                />
                <img src={this.state.imageUrl}/>
                 <button>Submit Dish</button> 
             </form> 
            </div>
        )
    }
};

export default AddNewDish

  

Один серверный

 
const multer = require('multer');

const fileStorage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, 'public')
    },
    filename: (req, file, cb) => {
        cb(null, new Date().toISOString()   '-'   path.extname(file.originalname))
    }
});

const fileFilter = (req, file, cb) => {
    if(
        file.mimetype === 'image/png' ||
        file.mimetype === 'image/jpg' || 
        file.mimetype === 'image/jpeg'
        ) {
            cb(null, true)
    } else {
        cd(null, false)
    }
}
exports.upload = multer({

        storage: fileStorage, 
        fileFilter: fileFilter
    })
        .single('imageUrl')

  

Так я называю свой маршрут

 exports.postADish = async  ( req, res,) => {
    console.log(req.file, 'in req.file')

    try {
        const { name,  description } = req.body;
        const imageUrl = req.file.path

            const newDish = await dish.postNewDish({name, description, imageUrl})
            res.status(201).json(`new dish added`)

    } catch (err) {
        res.status(500).json(`Error posting dish`)
        console.log(err)
    }
};

  

Как я использую промежуточное программное обеспечение для загрузки

 router.post('/add-new-dish', dishController.upload, dishController.postADish);
  

making folder public

 app.use('/public', express.static('public'))
  

This is how I’m trying to display the images in my index route on the frontend

 class App extends Component {
  constructor () {
    super()
    this.state = {
      data: []

    }

  }

    componentDidMount()  {
      axios
        .get(`http://localhost:8080/`)
        .then(res => {
          console.log(res, 'response')
          this.setState({
            data: res.data.dishData
          })
        })
        .catch(err => {
          console.log(err)
        })
  }


  render() {
   console.log(this.state.data)
    return (
      <div className="App">
        <Nav

         <Route
            exact path ='/'
            render={props =>
              <Home 
                {...props}
                dishes={this.state.data}

              />
            }
          />
  

Мое состояние выглядит так

 {id: 7, name: "test dish", imageUrl: "public/2019-04-20T22:52:09.900Z-20190411_112505.jpg", description: "test description"}

  

Я возвращаю ImageUrl и использую его в домашнем компоненте следующим образом

 const Home = (props) => {
    return (
        <DishWrapper>
            <DishContent>
           {props.dishes.map(dish => {
               console.log(dish, 'in dish')
               return (

                   <Dish key={dish.id}>
                        <h3>{dish.name}</h3>
                        <img src={ `public/${dish.imageUrl}`} alt={dish.name}/>
                        <h5>{dish.description}</h5>
                   </Dish>

               )
           })}
            </DishContent>
        </DishWrapper>
    )
};

export default Home

  

В серверной части мой get выглядит следующим образом

 exports.getDishes = async (req, res) => {
    try {
        const dishData = await dish.getDishes()
        if(!dishData) {
            res.status(404).json(`No dishes available at this time`)
        } else {
            res.status(200).json({
                dishData, 

            })
        }
    } catch (err) {
        res.status(500)
        console.log(err)
    }
};

  

Комментарии:

1. Мне кажется, что вы отправляете ImageUrl из своего компонента в вызове axios в multer, а затем добавляете строку даты к имени файла перед загрузкой, это правильно? Если это так, вам нужно вернуть это имя файла в вашем res и использовать его в качестве src изображения. Что вы получаете, когда вы консольный запрос журнала. файл в вашем маршруте вы получаете обновленное имя файла?

2. Это то, что я получаю в своем запросе. файловый консольный журнал { fieldname: 'imageUrl', originalname: '20190411_112542.jpg', encoding: '7bit', mimetype: 'image/jpeg', destination: 'images', filename: '2019-04-20T01:00:07.965Z-.jpg', path: 'images/2019-04-20T01:00:07.965Z-.jpg', size: 2361225 }

3. Итак, в моем .json я должен отправлять ImageUrl?

4. Итак, я только что заметил, что после загрузки изображения вы меняете страницу на индекс. У вас возникли проблемы с просмотром изображения до нажатия кнопки отправки или после? Если это раньше, вам нужно использовать другой подход, если это после, тогда вам нужно отправить имя файла изображения обратно в ваш res.

5. У меня проблемы с просмотром изображения как до, так и после

Ответ №1:

Итак, чтобы просмотреть изображение перед загрузкой, вам нужно сначала использовать FileReader, который является объектом javascript, который позволяет веб-приложениям асинхронно считывать содержимое файлов. Это даст вам версию вашего изображения на base64, которую вы можете добавить в свой src изображения. Итак, вы можете сделать это в своей функции onChange, и это будет выглядеть примерно так:

 onChange = (e) => {
    switch(e.target.name) {

        case 'imageUrl':
            const file = e.target.files[0];
            const reader = new FileReader();
            reader.onload = () => {
                this.setState({
                    imageUrl: file,
                    imgBase64: reader.result
                })
            };
            reader.readAsDataURL(file);
            break;
        default:
            this.setState({[e.target.name]: e.target.value})
    }
}
  

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

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

 <img src={this.state.imgBase64}/>
  

После этого, когда вы отправляете свое изображение в multer, вам нужно вернуть имя файла изображения, чтобы вы могли получить к нему доступ после его загрузки, поэтому в вашем маршруте, поскольку кажется, что вы получаете правильное имя файла при его регистрации, вы можете вернуть его в свой вызов axios и использовать его после этого. Итак, в вашем маршруте просто отправьте обратно объект json и используйте его. Поэтому вместо res.status(201).json( добавления нового блюда ) отправьте что-то вроде следующего:

 res.status(201).json({
  msg: 'new dish added',
  imageUrl: req.file.filename
})
  

и тогда ваш вызов axios получит этот объект json, и впоследствии вы сможете получить доступ к своему интерфейсу следующим образом:

 createDishHandler = (event) => {
    event.preventDefault()
    const fd = new FormData();
    fd.append('name', this.state.name)
    fd.append('imageUrl', this.state.imageUrl, this.state.imageUrl.name)
    fd.append('description', this.state.description)
    axios.post('http://localhost:8080/add-new-dish', fd,)
        .then(res => {
            //you can set your res.data.imageUrl to your state here to use it
            console.log('Success Message: '   res.data.msg   ' - Image File Name: '   res.data.imageUrl)
        })
    this.props.history.push('/')
}
  

Но в приведенной выше функции я вижу, что при загрузке вы нажимаете «Сделать другую страницу». Поэтому, если вы перейдете на другую страницу, вы, очевидно, больше не будете использовать это состояние, поэтому вам, вероятно, потребуется просто извлечь его из своей базы данных в этот момент с новой страницы.

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

PS Я просто хотел сообщить вам, что вы можете захотеть использовать Date.now() path.extname(file.originalname) вместо new Date().toISOString() '-' path.extname(file.originalname) того, чтобы загружать multer, он выглядит немного чище без всех двоеточий и тире, но в этом нет необходимости.

Редактировать:

Итак, если вы собираетесь обслуживать статическую папку с помощью express, то, как я уже говорил в предыдущих комментариях ниже, вам придется использовать абсолютные URL-адреса для доступа к вашему контенту. React не может получить доступ к чему-либо с относительными путями за пределами общей папки в вашем клиенте. Итак, если в корневом каталоге вашего сервера у вас будет папка с именем images, то в multer вы должны указать адрес назначения 'images' с помощью express, вы будете обслуживать статическую папку app.use(express.static('images')) , и для доступа к ней с помощью вашего изображения вам нужно будет использовать абсолютный URL

 <img src={ `http://localhost:8080/${imageUrl}`} alt={dish.name}/>
  

Комментарии:

1. Спасибо за помощь. Изображение теперь отображается, но не отправляется обратно на серверную часть. Я получаю сообщение `Не удается прочитать свойство «путь» неопределенного ` ` . Он отлично работает в postman, но не на интерфейсе. Я console log (fd) (formDate), и он возвращает пустой объект. Не уверен, что это проблема.

2. Вероятно, это потому, что я забыл установить для файла изображения значение ImageUrl в вашем состоянии во время функции onchange, потому что вы передаете его как файл в данные формы, извините, там было много, через что нужно пройти. Я отредактировал ответ, чтобы включить это. Попробуйте и посмотрите, что произойдет

3. Я пытаюсь отобразить изображения на домашней странице, но они не отображаются. Я удалил нажатие. Это было бы не в том месте. Извините, если я вас беспокою, я никогда раньше не работал с загрузкой файлов. Я отредактировал свой could, чтобы включить запрос get для внешнего интерфейса и внутреннего интерфейса.

4. Где находится папка, в которую вы также загружаете свои изображения? Он должен быть в общей папке. Если вы разместите его в другом месте, вы не сможете получить к нему доступ, потому что, если вы не можете получить доступ к любому другому месту в своем приложении через URL. Итак, если ваш файл изображений находится в общедоступной папке, то изображение должно выглядеть следующим <img src="/images/yourimage.jpg"/> образом, но, судя по всему, вы просто загружаете в папку images, которая находится в том же каталоге, что и ваш скрипт multer.

5. Свертка изображений находится в корне серверной части, но не в общей папке. Я думал, что, сделав папку изображений статической, я смогу получить к ней доступ?