我正在尝试显示通过multer上传到后端的图像

时间:2019-04-20 01:36:26

标签: mysql node.js reactjs multer

我正在使用multer从我的react客户端上传图像。所有图像都在存储中,只是没有显示出来。图像正在存储,但没有出现在反应中。处于设置状态只是不显示。我可以在后端的images文件夹中看到它们,所以我知道multer正在工作。

这是我的反应组件

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);

公开文件夹

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

这就是我试图在前端的索引路由中显示图像的方式

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并在home组件中使用它

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

在后端,我得到的像这样

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 个答案:

答案 0 :(得分:0)

因此,要在上传之前查看图像,您需要首先使用FileReader,这是一个javascript对象,可让Web应用程序异步读取文件内容。这将为您提供映像的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('/')
}

但是在上面的功能中,我看到您在上传时推送了另一页。因此,如果您推送到另一个页面,那么显然您将不再使用此状态,因此您可能需要在那时从新页面的数据库中检索它。

无论如何,我希望这会有所帮助,如果您有任何疑问,请告诉我。

P.S。我只是想让您知道,您可能希望在混合模式上传中使用Date.now() + path.extname(file.originalname)而不是new Date().toISOString() + '-' + path.extname(file.originalname),这样看起来有点干净,没有冒号和破折号,但这不是必须的。

编辑:

因此,如果您要使用express为静态文件夹提供服务,则就像我在下面的前面的评论中所说,您将必须使用绝对URL来访问您的内容。 React无法访问客户端中公共文件夹之外具有相对路径的任何内容。因此,如果在后端根目录中将有一个名为images的文件夹,那么在multer中,您可以将目标设置为'images'并表示您将提供静态文件夹app.use(express.static('images'))并通过您的图像进行访问您将需要使用绝对网址

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