简体   繁体   中英

How to display an image with <img> from Mongoose using React front-end

Ultimate goal: have the user upload pictures (less than 16mb so no need to worry about Grid FS), have that picture stored in my database which is Mongodb through Mongoose, and display the picture on the screen using the attribute.

To upload files I use Multer and add it to the database as follows:

newItem.picture.data = Buffer(fs.readFileSync(req.file.path), 'base64');
newItem.picture.contentType = 'image/png';

And it seems to be successfully added to the mongodb. Looks something like this: how the image appears on mongodb

I'm able to send a get request from my front-end and, when I console.log it, this is what I'm getting: Data after being retreived from database . The question now is, how can I add it to an attribute and show the image on the screen. Thanks!

Edit: question has been marked as too broad by the moderators. Fair enough, I wasn't too sure how to approach it. Since I was able to solve it, this is what my front-end looks like.

componentDidMount() {
    const PATH = "http://localhost:8080/apii/items/getitems";
    axios.get(PATH)
    .then(res => {            
        let picture64Bit = res.data[0].data.data
        picture64Bit = new Buffer(x, 'binary').toString('base64');
        this.setState({picture: picture64Bit})
    })
    .catch(err => console.log(err))
}

The key here is that, 1) res.data[0].data.data is equal to that random list of numbers. I take that convert it back to base64, so it appears exactly as it did in the first picture above from mongodb. Then, displaying it inline in an img attribute is very easy:

<img src = {`data:image/png;base64,${this.state.picture}`} /> 

There are a couple libraries you could use, but I will arbitrarily select Axios for a demonstration. It sounds good if the images are already in Mongo DB.

Your objective is to get photos from the server to the client, so you need a function to get them on demand. You could also investigate fetch or request .

Axios: https://www.npmjs.com/package/axios

In React, try something like this

  async getPhotos() {
    const res = await Axios.get('/photos')
    console.log('RESPONSE', res)

    const photos = res.data
    console.log('IMAGES', photos)

    this.setState({ photos })
  }

Here is a more complete example

import React, { Component } from 'react'
import Axios from 'axios'

class List extends Component {
  constructor(props) { // super props allows props to be available 
    super(props)       // inside the constructor
    this.state = {
      photos : [],     // Initialize empty list to assert existence as Array type
                       // and because we will retrieve a list of jpegs
      error: '',       // Initialize empty error display
    }
  }

  componentDidMount() {
    this.getPhotos()   // Do network calls in componentDidMount
  }

  async getPhotos() {
    try {
      const res = await Axios.get('/photos')
      console.log('RESPONSE', res)

      const photos = res.data
      console.log('IMAGES', photos)

      this.setState({ photos, error: '' })
    } catch (e) {
      this.setState({ error: `BRUTAL FAILURE: ${e}` })
    }
  }

  render() {
    if (error.length) {
      return (
        <div>{this.state.error}</div>
      )
    }
    if (!photos.length) {
      return (
        <div>No photos yet</div>
      )
    }
    // Assuming shape { id: 0, caption: 'Cats again', src: 'http://www.com/win.jpg' }
    // Make sure to include key prop when using map (for state management)
    return (
      <ul>
        {this.state.photos.map(photo => (
          <li key={photo.id} style={{ position: 'relative' }}>
            <span>{photo.caption}</span>
            <img src={photo.src}
            <div
              className="overlay"
              style={{
                position: 'absolute'
                width: '100%',
                height: '100%',
              }}
            />
          </li>
        ))}
      </ul>
    )
  }
}

Citation : In React.js should I make my initial network request in componentWillMount or componentDidMount?

If you want to fetch one more photo after, you should try to think immutably and replace the this.state.photos Array with a duplicate of itself plus the new image pushed onto the end of the array. We will use the spread operator for this to do a shallow copy on the existing photos Array. This will allow React to diff against the two states and efficiently update for the new entry.

const res = await Axios.get('/photo?id=1337')
const photo = res.data

this.setState({
  photos: [...photos, photo]
})

Note : the secret trick is to avoid ever doing this.state.photos.push(photo) . You must place an illegal sign on setting state like that.

In React, try to consider a way you can get an Object or Array. Once you have it in your mind, throw it into a Component's state. As you progress into Redux, you will end up storing items sometimes in the Redux store. That is too complex and unnecessary to describe now. The photos would be available perhaps as this.props.photos via the Redux Connect Function.

For most other times, a Component's state field is an excellent place to store anything of interest to a Component.

You can imagine it like a holder at the top of the Component.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM