You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 17图片上传提交表单时报TypeError错误求助

Angular图片上传FormData错误排查与解决

问题描述

跟随教程实现Angular图片上传功能时,提交表单后Chrome控制台抛出错误:

TypeError: Argument 2 ('blobValue') to FormData.append must be an instance of Blob

后端路由代码(Node.js + Express + Multer)

const express = require("express");
const router = express.Router();
const Post = require("../models/post");
const multer = require("multer");

const MIME_TYPE_MAP = {
  "image/png": "png",
  "image/jpeg": "jpg",
  "image/jpg": "jpg",
};

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "Api/upload");
  },
  filename: (req, file, cb) => {
    const name = file.originalname.toLowerCase().split(" ").join("-");
    const extension = MIME_TYPE_MAP[file.mimetype];
    cb(null, name + "-" + Date.now() + "." + extension);
  },
});

/* Here i post to the Mongo database  */
router.post(
  "",
  multer({ storage: storage }).single("image"),
  (req, res, next) => {
    const post = new Post({
      title: req.body.title,
      content: req.body.content,
    });
    post.save().then((createdPost) => {
      //console.log(result);
      res.status(201).json({
        message: "Post added succesfully to MongoDB!",
        postId: createdPost._id,
      });
    });
    //console.log(post);
  }
);

/* Here i fetch from the Mongo database  */
router.get("", (req, res, next) => {
  Post.find().then((documents) => {
    res
      .status(200)
      .json({ message: "Post fetched Successfully!", posts: documents });
  });
});

/* Here i delete from the Mongo database */
router.delete("/:id", (req, res, next) => {
  /* console.log(req.params.id);
  res.status(200).json({ message: "Post has een deleted!" }); */
  Post.deleteOne({ _id: req.params.id }).then((result) => {
    console.log(result);
    res.status(200).json({ message: "Post has een deleted!" });
  });
});

router.put("/:id", (req, res, next) => {
  const post = new Post({
    _id: req.body.id,
    title: req.body.title,
    content: req.body.content,
  });
  Post.updateOne({ _id: req.params.id }, post).then((result) => {
    // console.log(result);
    res.status(200).json({ message: "Update Post Successful!" });
  });
});

router.get("/:id", (req, res, next) => {
  Post.findById(req.params.id).then((post) => {
    if (post) {
      res.status(200).json(post);
    } else {
      res.status(404).json({ message: "Post not Found!" });
    }
  });
});

module.exports = router;

Angular PostService代码

import { Injectable } from '@angular/core';
import { Post } from './postModel';
import { Subject } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root',
})
export class PostServiceService {
  private posts: Post[] = [];
  private postsUpdated = new Subject<Post[]>();

  constructor(private http: HttpClient) {}

  /*  getPost(): Observable<Post[]> {
    const posts = of(this.posts);
    return posts;
  } */

  /* Here I get posts route */
  getPost() {
    //return [...this.posts];
    this.http
      .get<{ message: string; posts: any }>('http://localhost:4000/api/posts')
      .pipe(
        map((postData) => {
          return postData.posts.map((post: any) => {
            return {
              title: post.title,
              content: post.content,
              id: post._id,
            };
          });
        })
      )
      .subscribe((transformedtData) => {
        this.posts = transformedtData;
        this.postsUpdated.next([...this.posts]);
      });
  }

  getPostUpdatedListener() {
    return this.postsUpdated.asObservable();
  }

  getPosts(id: any) {
    return this.http.get<{ _id: string; title: string; content: string }>(
      'http://localhost:4000/api/posts/' + id
    );
  }

  addPost(title: string, content: string, image: File) {
    /*     const post: Post = {
      id: id,
      title: title,
      content: content,
    }; */
    const postData = new FormData();
    postData.append('title', title);
    postData.append('content', content);
    postData.append('image', image, title);

    this.http
      .post<{ message: string; postId: string }>(
        'http://localhost:4000/api/posts',
        postData
      )
      .subscribe((responseData) => {
        const post: Post = {
          id: responseData.postId,
          title: title,
          content: content,
        };
        /*   const id = responseData.postId;
        post.id = id; */
        //console.log(responseData.message);
        this.posts.push(post);
        this.postsUpdated.next([...this.posts]);
      });
  }

  updatePost(id: string, title: string, content: string) {
    const post: Post = { id: id, title: title, content: content };
    this.http
      .put('http://localhost:4000/api/posts/' + id, post)
      .subscribe((response) => /*console.log(response)*/ {
        const updatedPosts = [...this.posts];
        const oldPostsIndex = updatedPosts.findIndex((p) => p.id === post.id);
        updatedPosts[oldPostsIndex] = post;
        this.posts = updatedPosts;
        this.postsUpdated.next([...this.posts]);
      });
  }

  deletePost(postId: string) {
    this.http
      .delete('http://localhost:4000/api/posts/' + postId)
      .subscribe(() => {
        const updatedPosts = this.posts.filter((post) => post.id !== postId);
        this.posts = updatedPosts;
        this.postsUpdated.next([...this.posts]);
      });
  }
}

问题原因与解决方法

原因分析

错误提示FormData.append的第二个参数必须是Blob实例,说明调用addPost方法时传入的image参数不是有效的File/Blob对象。常见场景:

  • 模板中文件输入框绑定错误,未正确获取File对象
  • 传递参数时误传了其他类型值(如字符串、undefined)

解决步骤

  1. 修正组件模板的文件输入绑定
    确保文件输入框正确绑定change事件,获取选中的文件:

    <input type="file" (change)="onImagePicked($event)" accept="image/*">
    

    在组件类中实现方法保存File对象:

    selectedImage: File | undefined;
    
    onImagePicked(event: Event) {
      const file = (event.target as HTMLInputElement).files?.[0];
      if (file) {
        this.selectedImage = file;
      }
    }
    
  2. 调用addPost前校验参数
    调用服务方法前,确保selectedImage有效:

    onAddPost(title: string, content: string) {
      if (!this.selectedImage) {
        // 提示用户选择图片
        return;
      }
      this.postService.addPost(title, content, this.selectedImage);
    }
    
  3. 优化服务中的参数校验
    在addPost方法中增加校验,避免无效调用:

    addPost(title: string, content: string, image: File) {
      if (!(image instanceof File)) {
        console.error('Invalid image file:', image);
        return;
      }
      const postData = new FormData();
      postData.append('title', title);
      postData.append('content', content);
      postData.append('image', image, image.name); // 建议用图片原文件名而非title
      // 后续请求代码
    }
    
  4. 后端补充图片路径存储(可选)
    当前后端仅保存标题和内容,需修改Post模型添加imagePath字段,并在保存时存入路径:

    const post = new Post({
      title: req.body.title,
      content: req.body.content,
      imagePath: req.file.path // 可拼接完整访问URL,如`http://localhost:4000/${req.file.path}`
    });
    

内容的提问来源于stack exchange,提问作者Zayno lane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 11:22:03