【问题标题】:Uploading profile pic with react.js node.js express.js REST API to sqlite DB使用 react.js node.js express.js REST API 将配置文件图片上传到 sqlite DB
【发布时间】:2020-04-30 05:56:43
【问题描述】:

所以我试图通过 react.js 应用程序将配置文件图片上传到作为 REST API 运行的后端,使用 node.js express.js 和 sqlite 作为数据库。

但不知何故,我找不到任何适合我的教程。不过我一定很近。 所以我现在拥有的是这样的:

我的路线如下所示:
playerRoutes.js

const express = require('express');
const router = express.Router();
const multer = require('multer');
const uuidv4 = require('uuid/v4');

const upload_dir = './uploads';

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, upload_dir);
  },
  filename: (req, file, cb) => {
    cb(null, `${uuidv4()}-${file.filename.toLowerCase}`);
  }
});

const upload = multer({
  storage: storage,
  fileFilter: (req, file, cb) => {
    if (
      file.mimetype == 'image/png' ||
      file.mimetype == 'image/jpg' ||
      file.mimetype == 'image/jpeg'
    ) {
      cb(null, true);
    } else {
      cb(null, false);
      return cb(new Error('Only .png, .jpg and .jpeg format allowed!'));
    }
  }
});

const { playerController } = require('../controller');

router.get('/', playerController.getAllPlayer);
router.get('/:id', playerController.getSpecificPlayer);
router.post('/', upload.single('profileImg'), playerController.createPlayer);
router.put('/:id', playerController.updatePlayer);
router.delete('/:id', playerController.deletePlayer);

module.exports = router;

所以重点是storage 的设置,它告诉上传到哪里+upload 中的文件过滤器,对吧?

还有route.post,它将`upload.single('profileImg'),对吗?该路线将包括我的用于创建播放器的控制器,可以在此处找到:

playerController.js

const { Player } = require('../models');

module.exports = {
  getAllPlayer(req, res) {
    return Player.findAll({
      attributes: ['id', 'name', 'nickname', 'profileImg']
    })
      .then(players => res.status(200).send(players))
      .catch(err => res.status(400).send(err));
  },
  getSpecificPlayer(req, res) {
    return Player.findByPk(req.params.id, {
      attributes: ['id', 'name', 'nickname', 'profileImg']
    }).then(player => {
      if (!player) {
        return res.status(404).send({
          message: `Player with id ${req.params.id} not found`
        });
      }
      return res.status(200).send(player);
    });
  },
  createPlayer(req, res) {
    console.log(req.file);
    return Player.create({
      name: req.body.name,
      nickname: req.body.nickname
    })
      .then(player => res.status(201).send(player))
      .catch(err => res.status(400).send(err));
  },
  updatePlayer(req, res) {
    return Player.findByPk(req.params.id, {
      attributes: ['id', 'name', 'nickname', 'profileImg']
    })
      .then(player => {
        if (!player) {
          return res.status(404).send({
            message: `Player with id ${req.params.id} not found`
          });
        }
        return player
          .update({
            name: req.body.name || player.name,
            nickname: req.body.nickname || player.nickname,
            profileImg: req.body.profileImg || player.profileImg
          })
          .then(() => res.status(200).send(player))
          .catch(err => res.status(400).send(err));
      })
      .catch(err => res.status(400).send(err));
  },
  deletePlayer(req, res) {
    return Player.findByPk(req.params.id, {
      attributes: ['id', 'name', 'nickname', 'profileImg']
    })
      .then(player => {
        if (!player) {
          return res.status(404).send({
            message: `Player with id ${req.params.id} not found`
          });
        }
        return player
          .destroy()
          .then(() => res.status(204).send())
          .catch(err => res.status(400).send(err));
      })
      .catch(err => res.status(400).send(err));
  }
};

目前,关于几个教程,我应该在console.log(req.file) 中看到信息,当用帖子点击端点时,对吧?

模型如下所示:

models/player.js(续集模型)

'use strict';
module.exports = (sequelize, DataTypes) => {
  const Player = sequelize.define(
    'Player',
    {
      name: {
        type: DataTypes.STRING,
        validate: { notEmpty: { msg: 'Player name cannot be empty' } }
      },
      nickname: {
        type: DataTypes.STRING
      },
      profileImg: {
        type: DataTypes.STRING
      }
    },
    {}
  );
  Player.associate = function(models) {
    // associations can be defined here
  };
  return Player;
};

现在到前端代码(react.js):

这是我在我的 react 应用中加载的表单:

playerform.js

import React, { Component } from 'react';
import axios from 'axios';
import {
  FormControl,
  FormGroup,
  FormLabel,
  Form,
  Button
} from 'react-bootstrap';
import PropTypes from 'prop-types';

class PlayerForm extends Component {
  state = {
    name: '',
    nickname: '',
    profileImg: ''
  };

  handleSubmit = e => {
    e.preventDefault();

    axios
      .post(`${process.env.API_URL}/player`, JSON.stringify(this.state), {
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/json'
        }
      })
      .then(() => this.props.onCreate())
      .catch(err => console.log(err));
  };

  onFileChange(e) {
    this.setState({ profileImg: e.target.files[0] });
  }

  handelChange = e => {
    this.setState({
      [e.target.name]: e.target.value
    });
  };

  render() {
    return (
      <div className="container">
        <Form onSubmit={this.handleSubmit}>
          <FormGroup>
            <FormLabel>Name</FormLabel>
            <FormControl
              type="text"
              name="name"
              placeholder="Enter player name"
              onChange={this.handelChange.bind(this)}
            />
          </FormGroup>
          <FormGroup>
            <FormLabel>Nickname</FormLabel>
            <FormControl
              type="text"
              name="nickname"
              placeholder="Enter player nickname"
              onChange={this.handelChange.bind(this)}
            />
          </FormGroup>
          <FormGroup>
            <FormLabel>Picture</FormLabel>
            <FormControl
              type="file"
              name="file"
              playerholder="Upload player picture"
              onChange={this.onFileChange.bind(this)}
            />
          </FormGroup>
          <Button variant="btn btn-primary" type="submit">
            Add
          </Button>
        </Form>
      </div>
    );
  }
}

PlayerForm.propTypes = {
  onCreate: PropTypes.func.isRequired
};

export default PlayerForm;

因此,当输入内容并点击Add 按钮时,我的 api 指出 req.file 未定义,我无法找出原因。

谁能帮我找出错误?

【问题讨论】:

    标签: node.js reactjs rest express sequelize.js


    【解决方案1】:

    multer 的预期内容与您从前端实际发布的内容略有不同。

    如果您使用Postman(或类似名称),请尝试将 POST 请求以multipart/form-data 发送到您的“创建播放器”端点。您可能会看到它有效。

    但是,您的 axios 帖子以 'Content-Type': 'application/json' 发送数据,multer 包无法处理。

    尝试使用new FormData() 构造,如这里讨论的How do I set multipart in axios with react?

    【讨论】:

    • 所以我想我越来越近了。我现在在尝试发帖时收到no such file or directory, open 'uploads/64e5d50f-b0ab-4238-96b8-9a1ebe775748-undefined。它看起来像我的存储常量不会读取 file.filename。为什么?
    【解决方案2】:

    让它工作。相关部分是:

    playerform.js

    handleSubmit = e => {
        e.preventDefault();
        const formData = new FormData();
        formData.append('name', this.state.name);
        formData.append('nickname', this.state.nickname);
        formData.append('profileImg', this.state.profileImg);
    
        axios
          .post(`${process.env.API_URL}/player`, formData, {
            headers: {
              Accept: 'application/json',
              'Content-Type': 'multipart/form-data'
            }
          })
          .then(() => this.props.onCreate())
          .catch(err => console.log(err));
      };
    

    必须使用 FormData 并作为 Content-Type 'multipart/form-data'。

    然后我的上传文件夹出错了,因为它被动态引用(错误)而不是静态引用(必须是'./api/uploads')

    然后在 playerController.js 中使用console.log(req.file),我终于得到了图像数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-20
      • 1970-01-01
      • 2020-09-05
      • 2016-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多