У меня есть приложение MERN, и я пытаюсь сохранить файлы локально, однако, похоже, ничего не работает.
Я пытаюсь загрузить массив из нескольких images
.
Вот то, что у меня есть код:
collection.js
const mongoose = require("mongoose");
let collectionSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
reference: {
type: String,
required: true
},
images: [{ filename: String, mimetype: String, data: Buffer }],
price: {
type: Number,
required: true
},
year: Number,
categoryName: {
type: String,
required: true
},
sold: {
type: Boolean,
default: false
}
});
collectionSchema.virtual("category", {
ref: "category",
localField: "name",
foreignField: "categoryName",
justOne: false
});
module.exports = mongoose.model("collection", collectionSchema);
collectionController.js POST метод
/* POST create a collection */
module.exports.createCollection = [
...validations,
(req, res) => {
// Get validation errors from the request
const errors = validationResult(req);
// Return the errors
if (!errors.isEmpty()) {
return res.status(422).json({ error: errors.array() });
}
Collections.findOne({ _id: req.body.id }).then(function(collection) {
Collections.create({
title: req.body.title,
description: req.body.description,
reference: req.body.reference,
images: req.body.images,
price: req.body.price,
year: req.body.year,
categoryName: req.body.categoryName,
sold: req.body.sold
})
.then(function(collection) {
res.status(201).json(collection);
})
.catch(function(error) {
console.error(error);
res.status(500).send(error);
});
});
}
];
маршруты
const express = require("express");
const router = express.Router();
const passport = require("passport");
const collection = require("../controllers/collectionsController");
const multer = require("multer");
const GridFsStorage = require("multer-gridfs-storage");
const crypto = require("crypto");
const authenticate = passport.authenticate("jwt", { session: false });
// Create storage engine
const storage = new GridFsStorage({
url: process.env.MONGODB_URI,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = file.originalname;
const fileInfo = {
filename: filename,
bucketName: "uploads"
};
resolve(fileInfo);
});
});
}
});
const upload = multer({ storage });
router.get("/", collection.index);
router.get("/:id", collection.getCollection);
// CRUD routes
router.post(
"/create",
authenticate,
upload.single("images"),
collection.createCollection
);
router.put("/:id", authenticate, collection.updateCollection);
router.delete("/:id", authenticate, collection.deleteCollection);
module.exports = router;
create component
import React, { Component } from "react";
import axios from "axios";
import swal from "sweetalert";
import AuthService from "../../Auth/AuthService";
import withAuth from "../../Auth/withAuth";
const Auth = new AuthService();
class Create extends Component {
constructor() {
super();
this.state = {
title: "",
description: "",
reference: "",
images: [],
price: "",
year: "",
categoryName: "",
categorys: [],
sold: false,
titleErr: "",
descriptionErr: "",
referenceErr: "",
imagesErr: "",
priceErr: "",
yearErr: "",
categoryErr: ""
};
}
validate = () => {
let titleErr = "";
let descriptionErr = "";
let referenceErr = "";
let imagesErr = "";
let priceErr = "";
let yearErr = "";
let categoryNameErr = "";
// Title validation
if (!this.state.title) {
titleErr = "Please enter a title";
}
// Description validation
if (!this.state.description) {
descriptionErr = "Please enter a description";
}
// Reference validation
if (!this.state.reference) {
referenceErr = "Please enter a reference";
}
// Images validation
if (!this.state.images) {
imagesErr = "You must have at least one image";
}
// Price validation
if (!this.state.price) {
priceErr = "Please enter a price";
}
// Year validation
if (!this.state.year) {
yearErr = "Please enter a year";
}
// Category validation
if (!this.state.categoryName) {
categoryNameErr = "Please enter a category";
}
// Render validations
if (
titleErr ||
descriptionErr ||
referenceErr ||
imagesErr ||
priceErr ||
yearErr ||
categoryNameErr
) {
this.setState({
titleErr,
descriptionErr,
referenceErr,
imagesErr,
priceErr,
yearErr,
categoryNameErr
});
return false;
}
return true;
};
componentDidMount() {
axios
.get("/api/category")
.then(res => this.setState({ categorys: res.data }))
.catch(error => {
console.log(error);
});
}
onTitleChange(event) {
this.setState({ title: event.target.value });
}
onDescriptionChange(event) {
this.setState({ description: event.target.value });
}
onReferenceChange(event) {
this.setState({ reference: event.target.value });
}
onImagesChange(event) {
this.setState({ images: event.target.value });
}
onPriceChange(event) {
this.setState({ price: event.target.value });
}
onYearChange(event) {
this.setState({ year: event.target.value });
}
onCategoryNameChange(event) {
this.setState({ categoryName: event.target.value });
}
onSoldChange(event) {
this.setState({ sold: event.target.value });
}
onSubmit = e => {
e.preventDefault();
const isValid = this.validate();
const {
title,
description,
reference,
images,
price,
year,
categoryName,
sold
} = this.state;
let config = {
headers: { Authorization: "bearer " + Auth.getToken() }
};
let body = {
title,
description,
reference,
images,
price,
year,
categoryName,
sold
};
const file = document.getElementById("images").files;
const formData = new FormData();
formData.append(body, file);
if (isValid) {
axios
.post("/api/collections/create", formData, config)
.then(result => {
swal({
title: "Success",
text: "You have created a collection",
icon: "success",
button: "OK"
});
this.props.history.push("/dashboard");
})
.catch(error => {
swal({
title: "Error",
text: `${error}`,
icon: "error",
button: "Try again"
});
});
}
};
render() {
const {
title,
description,
reference,
images,
price,
year,
categoryName,
sold
} = this.state;
return (
<>
<div className='block md:flex md:flex-column h-full'>
<div className='p-12 w-full text-center text-gray-800'>
<h1 className='title mb-10'>Create a collection</h1>
<form
className='w-full m-auto max-w-lg'
onSubmit={this.onSubmit}
encType='multipart/form-data'
>
<div className='flex flex-wrap mb-4'>
<label htmlFor='title'>Title:</label>
<input
type='text'
name='title'
value={title}
onChange={this.onTitleChange.bind(this)}
placeholder='Title'
/>
<p className='mt-2 text-red-500 text-xs'>
{this.state.titleErr}
</p>
</div>
<div className='flex flex-wrap'>
<label htmlFor='description'>Description:</label>
<textarea
type='text'
name='description'
className='h-64'
value={description}
onChange={this.onDescriptionChange.bind(this)}
placeholder='Content'
></textarea>
<p className='mb-4 text-red-500 text-xs'>
{this.state.descriptionErr}
</p>
</div>
<div className='flex flex-wrap mb-4'>
<label htmlFor='reference'>Reference:</label>
<input
type='text'
name='reference'
value={reference}
onChange={this.onReferenceChange.bind(this)}
placeholder='reference'
/>
<p className='mt-2 text-red-500 text-xs'>
{this.state.referenceErr}
</p>
</div>
<div className='flex flex-wrap mb-4'>
<label htmlFor='images'>images:</label>
<input
type='file'
multiple
name='images'
value={images}
onChange={this.onImagesChange.bind(this)}
id='images'
></input>
<p className='mt-2 text-red-500 text-xs'>
{this.state.imagesErr}
</p>
</div>
<div className='flex flex-wrap mb-4'>
<label htmlFor='price'>Price:</label>
<input
type='number'
name='price'
value={price}
onChange={this.onPriceChange.bind(this)}
placeholder='price'
/>
<p className='mt-2 text-red-500 text-xs'>
{this.state.priceErr}
</p>
</div>
<div className='flex flex-wrap mb-4'>
<label htmlFor='year'>Year:</label>
<input
type='number'
name='year'
value={year}
onChange={this.onYearChange.bind(this)}
placeholder='Year'
/>
<p className='mt-2 text-red-500 text-xs'>
{this.state.yearErr}
</p>
</div>
<div className='flex flex-col mb-4'>
<label htmlFor='categoryName'>Category</label>
<div className='relative'>
<select
name='categoryName'
value={categoryName}
onChange={this.onCategoryNameChange.bind(this)}
>
<option>N/A</option>
{this.state.categorys.map(category => (
<option key={category._id} value={category.name}>
{category.name}
</option>
))}
</select>
<div className='pointer-events-none absolute inset-y-0 right-0 flex items-center px-2'>
<svg
className='fill-current h-4 w-4'
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 20 20'
>
<path d='M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z' />
</svg>
</div>
</div>
</div>
<div className='flex flex-col mb-2'>
<label htmlFor='sold'>Sold?</label>
<div className='relative'>
<select
name='sold'
value={sold}
onChange={this.onSoldChange.bind(this)}
>
<option value='false'>No</option>
<option value='true'>Yes</option>
</select>
<div className='pointer-events-none absolute inset-y-0 right-0 flex items-center px-2'>
<svg
className='fill-current h-4 w-4'
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 20 20'
>
<path d='M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z' />
</svg>
</div>
</div>
</div>
<div className='flex'>
<button type='submit' className='btn w-full'>
Submit
</button>
</div>
</form>
</div>
</div>
</>
);
}
}
export default withAuth(Create);
Если я тестирую создание коллекции в почтальоне, я получаю следующее
Любая помощь в этом была бы великолепной, как это былоя весь день везу меня на стену, пытаясь даже сохранить базовые локальные изображения, не говоря уже о S3.
Теперь я получаю