【问题标题】:Uploading file with multer (NodeJS) + other form data on same POST request (Angular)在同一 POST 请求(Angular)上使用 multer(NodeJS)+其他表单数据上传文件
【发布时间】:2020-09-21 16:27:28
【问题描述】:

我正在 Angular 构建前端提交表单。 该表单具有普通文本输入字段和文件上传功能。 我将文本输入字段作为 JSON 对象“联系人”发布到我的 NodeJS API,并将文件作为新的 FormData 发布:

// 'contact' defined above as a JSON object
// 'profilePic' set from event.target.files[0] in a listener function

const profilePicData = new FormData();
profilePicData.append('file', profilePic);

            return this.http
                .post<ContactResponseData>('API_URL_HERE',
                { contact, profilePicData } ...

然后像这样从我的 API 中捕获它:

router.post("/", upload.single('file'),(req, res) => {

    console.log("REQ: "+ req);

    console.log("BODY: " + JSON.stringify(req.body));
    console.log("FILE: " + req.file);

req.file 是“未定义”,即 null,而我的 req.body 有一个“profilePicData”键值对,它是空的。我认为这是因为整个表单是作为 JSON 而不是作为多部分表单数据提交的。

但是对于如何将 JSON 和 multipart 作为一个 POST 请求发送到我的 API 以便 req.body 和 req.file 都能获取正确的信息,我无法在 Google 上搜索任何有用的信息。我想了解这里发生的事情背后的理论和最佳实践是我所追求的。我应该有两个 POST url,一个用于 JSON,一个用于文件吗?或者我也应该将我的 JSON 作为多部分提交(我如何在 Angular 中做到这一点)?任何帮助表示赞赏。

【问题讨论】:

    标签: node.js angular upload multer multipart


    【解决方案1】:

    我在 React 中使用了下面的方法

    以下是我创建输入的方式

    <form className={styles.root} noValidate autoComplete="off">
        <input
            name="avatar_image" // name of input field or fieldName simply
            enctype="multipart/form-data"
            type="file"
            onChange={(event) => {
                // console logging selected file from menu
                console.log( event.target.files[0] ) // gives first file
                // setState method with event.target.files[0] as argument
                this.setState(prev => ({...prev, user_image: event.target.files[0]}))
            }}
        />
    </form>
    

    以下是我向后端发出请求的方式

    const formData = new FormData()
    formData.append('user_name', this.state.user_name)
    formData.append('phone_number', this.state.phone_number)
    // now below avatar_image is the fieldName of the image, then comes the file to upload, and the file name in the end
    formData.append('avatar_image', this.state.user_image, this.state.user_image.name)
    
    axios.post(utils.baseUrl + '/avatar-uploads/avatar-image-upload', formData, {
        onUploadProgress: progressEvent => {
            console.log( 'upload progress: ' + Math.round((progressEvent.loaded / progressEvent.total)*100) + '%' )
        }
    })
    .then(function (response) {
        // further code
    })
    .catch(function (error) {
        console.log(error);
    }); 
    

    下面是我如何使用multer处理backned,包括处理payload

    const image_storage = multer.diskStorage({
        destination: path.join(__dirname , '../../assets/images/uploads/avatar_image'),
        filename: function(req, file, cb){
            cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname))
        }
    });
    
    // Init Upload
    const user_avatar_image_upload = multer({
        storage: image_storage,
        limits:{fileSize: 2000000}, // 1 mb
        fileFilter: function(req, file, cb){
            checkFileTypeForUserAvatar(file, cb);
        }
    }).single('avatar_image'); // this is the fieldName that will be dealt
    
    // Check File Type
    function checkFileTypeForUserAvatar(file, cb){
        // Allowed ext
        let filetypes = /jpeg|jpg|png|gif/;
        // Check ext
        let extname = filetypes.test(path.extname(file.originalname).toLowerCase());
        // Check mime
        let mimetype = filetypes.test(file.mimetype);
    
        if(mimetype && extname){
            return cb(null,true);
        } else {
            cb('Error: jpeg, jpg, png, gif Images Only!');
        }
    }
    
    // router.post('/protected-avatar-image-upload', passport.authenticate('jwt', { session: false }), (req, res, next) => {
    router.post('/avatar-image-upload', (req, res, next) => {
    
        console.log(req.body) // here the req.body will turn out {}
    
        user_avatar_image_upload(req, res, (err) => {
            if(err){
                console.log(err)
            } else {
    
                if(req.file == undefined){
    
                    res.status(404).json({ success: false, msg: 'File is undefined!',file: `uploads/${req.file.filename}`})
    
                } else {
                    console.log( req.body.user_name ) // here req.body.user_name and others will work
                    // further code
                    res.status(200).json({ success: true, msg: 'File Uploaded!',file: `uploads/${req.file.filename}`})
                }
            }
        })
    
    })
    

    希望这有助于如何使用 multer 上传文件,并传递额外的有效负载,以便它可以用于创建数据库条目或其他任何内容。

    【讨论】:

      【解决方案2】:

      您必须通过向FormData 的实例添加字段并将其作为有效负载发送来将所有内容作为多部分发送。

      const form = new FormData();
      form.append('file', profilePic);
      form.append('contact', contact);
      
      ...
      return this.http.post<ContactResponseData>('API_URL_HERE', form, ...)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-05
        • 2017-03-29
        • 2020-12-31
        • 2018-07-21
        • 2016-06-29
        相关资源
        最近更新 更多