【发布时间】:2019-03-20 15:11:35
【问题描述】:
我是 react 新手,我没有看到任何有关使用 formdata (react) 上传图像的文档,而且我也不能在服务器 (nodejs) 上发送图像。如果你们中的任何人有东西,然后在这里发送mern栈图片上传
【问题讨论】:
我是 react 新手,我没有看到任何有关使用 formdata (react) 上传图像的文档,而且我也不能在服务器 (nodejs) 上发送图像。如果你们中的任何人有东西,然后在这里发送mern栈图片上传
【问题讨论】:
查看我的 git 存储库,其中包含图像上传反应前端和 node.js 服务器,以将图像上传到 AWS S3 存储桶。 https://github.com/ShanikaEdiriweera/image-upload-app
作为对@Nguyễn Thanh Tú 答案和 repo 中使用的方法(从反应组件调用 http 请求)的补充,这就是您应该如何使用 Redux 和动作创建者(redux-thunk 作为中间件)来处理 API 等副作用/server 调用。
组件代码:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as imageActions from '../actions/imageActions';
export default class WhatsApp extends Component {
...
handleSubmit = e => {
e.preventDefault();
if (!this.fileInput.current.files[0]) return;
let data = new FormData();
data.append('logo', this.fileInput.current.files[0]);
// calling the action creator
this.props.imageActions.imageUpload(data);
};
...
}
function mapDispatchToProps(dispatch) {
return {
imageActions: bindActionCreators(imageActions, dispatch),
...
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(WhatsApp);
imageActions.js 动作文件:
import axios from 'axios';
...
export const imageUpload = (data) => {
return dispatch => {
return axios.post('http://127.0.0.1:3100/what', data).then(
// here we can dispatch redux actions
// and then handle them in redux reducers
res => console.log(res.data),
err => console.log(err.message)
);
};
};
在动作创建器中,我们可以调度redux actions,然后在redux reducers中处理调度的动作,以在前端组件上创建效果。
【讨论】:
如果你对 hooks 和 redux 有反应,这就是我使用和工作的方式。
诀窍是使用 formData.set(),我之前使用过 formData.append 并且不起作用,将表单字段的状态值发送到 axios.post (我使用 axios.create)。
如果不需要用户认证,就不需要配置headers。
很重要!!!,图像字段,将是type='file',并且在状态下使用e.target.files[0],
在表单组件中:
//* AQUI ESTARÁ EL FORMULARIO PARA EL PRODUCTO
import styled from "styled-components";
import { useState } from "react";
import Select from "react-select";
import FormData from "form-data";
import { useDispatch } from "react-redux";
import "react-toastify/dist/ReactToastify.css";
//ACTIONS DE REDUX
import { crearNuevoProductoAction } from "../actions/productoActions";
const Label = styled.label`
font-family: Anton;
`;
const TextArea = styled.textarea`
font-family: Lato;
`;
const tablas = [
{ value: "slalom", label: "Slalom" },
{ value: "freeride", label: "Free-Ride" },
{ value: "freerace", label: "Free-Race" },
{ value: "freestyle", label: "Free-Style" },
{ value: "waves", label: "Waves" },
];
const velas = [
{ value: "slalom", label: "Slalom_V" },
{ value: "freeride", label: "Free-Ride" },
{ value: "freerace", label: "Free-Race" },
{ value: "freestyle", label: "Free-Style" },
{ value: "waves", label: "Waves" },
];
const botavaras = [
{ value: "carbono", label: "Carbono" },
{ value: "aluminio", label: "Aluminio" },
{ value: "mixtas", label: "Mixtas" },
];
const mastiles = [
{ value: "rdm", label: "RDM" },
{ value: "sdm", label: "SDM" },
];
const accesorios = [
{ value: "arnes", label: "Arnes" },
{ value: "alargador", label: "Alargador" },
{ value: "aleta", label: "Aleta" },
];
const NuevoProducto = () => {
//MANEJO DE STATES LOCALES
const [categoria, setCategoria] = useState("");
const handleProduct = (e) => {
setCategoria(e.target.value);
};
const [subCategoria, setSubCategoria] = useState("");
const handleSubProduct = (e) => {
setSubCategoria(e.value);
};
const [title, setTitle] = useState("");
const [price, setPrice] = useState("");
const [description, setDescription] = useState("");
const [images, setImage] = useState("");
console.log(images)
let subopcion;
switch (categoria) {
case "tabla":
subopcion = tablas;
break;
case "vela":
subopcion = velas;
break;
case "botavara":
subopcion = botavaras;
break;
case "mastil":
subopcion = mastiles;
break;
case "accesorio":
subopcion = accesorios;
break;
default:
subopcion = tablas;
break;
}
//MANEJO DEL REDUX EN EL FORMULARIO
//UTILIZAR USEDISPATCH Y TE CREA UNA FUNCION
const dispatch = useDispatch();
//manda llamar al action de productoAction
const agregarProducto = (producto) =>
dispatch(crearNuevoProductoAction(producto));
//AL HACER SUBMIT EN EL FORMULARIO
const submitNuevoProducto = (e) => {
e.preventDefault();
let formData = new FormData();
formData.set('images', images)
formData.set('title', title)
formData.set('categoria', categoria)
formData.set('subCategoria', subCategoria)
formData.set('price', price)
formData.set('description', description)
agregarProducto(formData)
};
return (
<div className="container mt-5">
<div className="row justify-content-center">
<div className="col-md-8">
<div className="card">
<div className="card-body">
<h2 className="text-center mx-auto font-wight-bold mb-5">
Agregar Nuevo Producto
</h2>
<form onSubmit={submitNuevoProducto}>
<div className="mb-3">
<Label className="mb-2">Selecciona el tipo de producto</Label>
<select
className="custom-select form-control"
defaultValue=""
name="categoria"
onChange={handleProduct}
>
<option value="" selected>
Selecciona el tipo de producto
</option>
<option value="tabla">Tabla</option>
<option value="vela">Vela</option>
<option value="botavara">Botavara</option>
<option value="mastil">Mastil</option>
<option value="accesorio">Accesorio</option>
</select>
</div>
<div className="mb-3">
<Label className="mb-2">Selecciona el tipo de producto</Label>
<Select
defaultValue=""
name="subCategoria"
onChange={handleSubProduct}
options={subopcion}
/>
</div>
<div className="mb-3">
<Label htmlFor="tituloProducto" className="form-label">
Producto
</Label>
<input
type="text"
className="form-control"
name="title"
id="title"
placeholder="Tabla Slalom ...."
onChange={(e) => setTitle(e.target.value)}
></input>
</div>
<div className="mb-3">
<Label htmlFor="precioProducto" className="form-label">
Precio
</Label>
<input
type="number"
className="form-control"
id="precioProducto"
placeholder="450"
name="price"
onChange={(e) => setPrice(Number(e.target.value))}
></input>
</div>
<div className="mb-3">
<Label htmlFor="descripcionProducto" className="form-label">
Descripción del Producto
</Label>
<TextArea
className="form-control"
id="descripcionProducto"
rows="3"
onChange={(e) => setDescription(e.target.value)}
></TextArea>
</div>
<div>
<input
className="form-input"
id="images"
type='file'
name="images"
onChange={(e) => setImage(e.target.files[0])}
></input>
</div>
<div className="mb-3 text-center">
<button className="btn btn-success" type="submit">
Agregar Producto
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
);
};
export default NuevoProducto;
在 actions.js 中
import clienteAxios from "../config/axios";
import Swal from 'sweetalert2';
import FormData from "form-data";
const user = JSON.parse(localStorage.getItem('user'));
const data = ({
headers: {
'x-auth-token': user,
},
//body: {imagenData},
})
console.log(user)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//CREAR NUEVOS PRODUCTOS
export function crearNuevoProductoAction(producto) {
console.log(producto)
return async (dispatch) => {
dispatch(agregarProducto());
try {
//INSERTAR EN LA API
await clienteAxios.post("/api/productos", producto, data);
//console.log(respuestaPost.data);
//SI TODO VA BIEN, SE ACTUALIZA EL STATE
dispatch(agregarProductoExito(producto));
//PONER AQUÍ LA ALERTA DE QUE SE CREO BIEN EL PRODUCTO
Swal.fire(
'Correcto',
'El Producto se subió Correctamente',
'success'
).then(function() {
window.location = "/productos"})
console.log(producto)
} catch (error) {
console.log(error);
dispatch(agregarProductoError(true));
}
};
}
const agregarProducto = () => ({
type: AGREGAR_PRODUCTO,
payload: true,
});
//SI EL PRODUCTO SE GUARDA EN LA BBDD
const agregarProductoExito = (producto) => ({
type: AGREGAR_PRODUCTO_EXITO,
payload: producto,
});
//SI HUBO UN ERROR CON EL PRODUCTO
const agregarProductoError = (estado) => ({
type: AGREGAR_PRODUCTO_ERROR,
payload: estado,
});
【讨论】: