Загрузка профиля pi c с реакцией. js node.js express. js REST API в sqlite DB - PullRequest
0 голосов
/ 13 января 2020

Поэтому я пытаюсь загрузить изображение профиля с помощью реакции. 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'), верно? Маршрут будет включать в себя мой контроллер для создания Player, который можно найти здесь:

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) при попадании в конечную точку с помощью сообщения, верно?

Модель выглядит следующим образом:

модели / игрок. 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;
};

Теперь перейдем к коду внешнего интерфейса (реакция. js):

Это форма, которую я загружаю в свое приложение реакции:

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 не определен, и я не могу понять, почему.

Может кто-нибудь помочь мне развернуть ошибку?

Ответы [ 2 ]

0 голосов
/ 13 января 2020

Получил это на работу. Соответствующие части:

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'.

Затем я допустил ошибку с моей папкой для выгрузки, так как на нее ссылались динамически (неправильно) вместо stati c (должно быть './api/uploads')

, а затем с console.log(req.file) в playerController. js Я наконец получаю данные изображения.

0 голосов
/ 13 января 2020

У вас небольшое несоответствие в том, что ожидает multer, и что вы на самом деле размещаете на своем внешнем интерфейсе.

Если вы используете Почтальон (или аналогичный ), попробуйте отправить запрос POST как multipart/form-data на конечную точку «Создать игрока». Вы, вероятно, увидите, что это работает.

Ваш топор ios, однако, отправляет данные как 'Content-Type': 'application/json', что пакет мультиплексора не будет обрабатывать.

Попробуйте использовать new FormData() построить как обсуждено здесь Как установить multipart в ax ios с реагировать?

...