簡體   English   中英

使用 Multer 提交表單時如何防止圖像保存

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM