[英]How to remove empty elements from a form in ember, when saving/submitting?
[英]How to Prevent an Image from Saving When Submitting Form Using Multer
我正在开发一个 Nodejs 项目,我正在尝试使用 multer 在本地存储图像。 我已经在 HTML 中制作了我的表格,并且能够让一切正常工作。 保存图像后,它们将存储在我创建的上传文件夹中。 但是,我遇到了将表单中的图像存储在上传文件夹中的问题,即使存在诸如导致重定向到表单页面的空字段之类的错误。 除非表格正确填写,否则是否有阻止图像保存的方法? 这是我的仓库的链接: https://github.com/halsheik/RecipeWarehouse.git 。 以下是将 multer 添加到项目中所做的编辑。
// Modules required to run the application
const express = require('express');
const multer = require('multer');
const crypto = require('crypto');
const path = require('path');
const { ensureAuthenticated } = require('../config/auth');
// Creates 'mini app'
const router = express.Router();
// Models
const Recipe = require('../models/Recipe'); // Recipe Model
// Set up storage engine
const storage = multer.diskStorage({
destination: function(req, file, callback){
callback(null, 'public/uploads');
},
filename: function(req, file, callback){
crypto.pseudoRandomBytes(16, function(err, raw) {
if (err) return callback(err);
callback(null, raw.toString('hex') + path.extname(file.originalname));
});
}
});
const upload = multer({
storage: storage
});
// My Recipes
router.get('/myRecipes', ensureAuthenticated, function(req, res){
Recipe.find({}, function(err, recipes){
if(err){
console.log(err);
} else {
res.render('./home/myRecipes', {
recipes: recipes,
ingredients: recipes.ingredients,
directions: recipes.directions
});
}
});
});
// Create Recipe Page
router.get('/createRecipe', ensureAuthenticated, function(req, res){
res.render('./home/createRecipe');
});
// Create Recipe
router.post('/createRecipe', upload.single('recipeImage'), ensureAuthenticated, function(req, res){
const { recipeName, ingredients, directions } = req.body;
let errors = [];
// Checks that all fields are not empty
if(!recipeName || !ingredients || !directions){
errors.push({ msg: 'Please fill in all fields.' });
}
// Checks that an image is uploaded
if(!req.file){
errors.push({ msg: 'Please add an image of your recipe' });
}
// Checks for any errors and prevents recipe creation if any
if(errors.length > 0){
console.log(errors);
res.render('./home/createRecipe', {
errors,
recipeName,
ingredients,
directions
});
} else {
// Create a new 'Recipe' using our model
const newRecipe = new Recipe({
recipeName: recipeName,
author: req.user._id,
ingredients: ingredients,
directions: directions,
});
// Saves recipe to mongoDB database
newRecipe.save().then(function(){
res.redirect('/recipes/myRecipes');
}).catch(function(err){
console.log(err);
});
}
});
module.exports = router;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Homemade</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div class="newRecipeContainer">
<form action="/recipes/createRecipe" method="POST" enctype="multipart/form-data">
<div class="recipeNameContainer">
<label class="recipeNameLabel">Title</label>
<input type="text" name="recipeName">
</div>
<div class="recipeImage">
<input type="file" accept="image/*" name="recipeImage" onchange="validateImageFile(this);"/>
</div>
<div class="ingredientsContainer">
<button class="addIngredientButton" type="button" @click="addIngredientForm">Add Another Ingredient</button>
<div class="allIngredients" v-for="(ingredient, ingredientIndex) in ingredients">
<label class="ingredient">{{ ingredientIndex + 1 }}.)</label>
<input type="text" name="ingredients" v-model="ingredient.ingredient">
<button class="deleteIngredientButton" type="button" v-if="ingredientIndex > 0" @click="deleteIngredientForm(ingredientIndex)">Delete Ingredient</button>
</div>
</div>
<div class="directionsContainer">
<button class="addDirectionButton" type="button" @click="addDirectionForm">Add Another Direction</button>
<div class="allDirections" v-for="(direction, directionIndex) in directions">
<label class="direction">{{ directionIndex + 1 }}.)</label>
<input type="text" name="directions" v-model="direction.direction">
<button class="deleteDirectionButton" type="button" v-if="directionIndex > 0" @click="deleteDirectionForm(directionIndex)">Delete Direction</button>
</div>
</div>
<button class="createRecipeButton" type="submit">Create Recipe</button>
</form>
</div>
<script src="/controls/newRecipeControl.js"></script>
</body>
</html>
谢谢你的帮助!
对于一个月前做的一个学校项目,我遇到了同样的问题。 我通过使用 multer memory 存储解决了这个问题,然后使用 multer 提供的缓冲区自己保存它。 有点愚蠢的解决方法,但它对我有用,而且由于你似乎和我有同样的问题,它也对你有用。
查看他们关于如何使用它的文档。 还可以查看如何使用 fs 模块将缓冲区写入文件。
编辑:
好的,我找到了代码:
export const validateRequest = (req, res, next, schema, fileExpected = false) => {
const options = { abortEarly: false, allowUnknown: true, stripUnknown: true };
const { error, value } = schema.validate(req.body, options);
const validationErrors = [];
if (fileExpected && req.file === undefined) validationErrors.push('"prod_image" is requiered.');
if (error) error.details.forEach(x => validationErrors.push(x.message));
if (validationErrors.length > 0) {
res.status(400).json(validationErrors);
} else {
req.body = value;
next();
}
};
由于 multer 同时填充 req.file 和 req.body,并且由于它需要在 joi 之前运行以处理多部分/表单数据,这就是我验证请求的方式。 在此之后,剩下的就是将文件持久化到磁盘。 我是这样做的:
import fs from 'fs';
import path from 'path';
import multer from 'multer';
import { randomBytes } from 'crypto';
import { srcPath } from './../settings';
const storage = multer.memoryStorage();
const fileFilter = (req, file, cb) => {
const ext = path.extname(file.originalname);
if (ext !== '.jpg' && ext !== '.png') return cb(new Error('Invalid image extension.'));
cb(null, true);
};
export const upload = multer({storage: storage, fileFilter: fileFilter });
export const persistImage = (file, cb) => {
const ext = path.extname(file.originalname);
const newName = randomBytes(16).toString('hex') + ext;
const imagesFolderPath = srcPath + '/productImages/';
const finalPath = path.join(imagesFolderPath, newName);
fs.writeFile(finalPath, file.buffer, (err) => cb(err, newName));
};
export const removeImage = (imageName, cb) => {
const imagesFolderPath = srcPath + '/productImages/';
const finalPath = path.join(imagesFolderPath, imageName);
fs.unlink(finalPath, (err) => cb(err));
};
如果将数据保存到数据库失败,则需要 removeImage function。 在我看来,这是一个非常糟糕的解决方案,但它是 class 的要求。 我的教授认为将图像保存在数据库中是邪恶的。 在实际场景中,您可能希望将它们保存到 Azures blob 存储或类似的东西。 那将是理想的,但是我的项目需要将文件保存在项目文件夹中,sooooo .....
这样做时,很多事情都会出错。 希望这会有所帮助,干杯。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.