我正在遵循 Angular 中的图像上传教程,但每当我发布表单时,我都会在 Chrome 开发控制台中收到此错误
TypeError:FormData.append 的参数 2 ('blobValue') 必须是 Blob 的实例
这是我路由到数据库的代码(Mongo Atlas):
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;
这是售后服务代码
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 中处理图像文件的方式存在问题。我对代码做了一些更改,如下所示:
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() {
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,
imagePath: post.imagePath // Add this to store image path
};
});
})
)
.subscribe((transformedData) => {
this.posts = transformedData;
this.postsUpdated.next([...this.posts]);
});
}
getPostUpdatedListener() {
return this.postsUpdated.asObservable();
}
getPosts(id: string) {
return this.http.get<{ _id: string; title: string; content: string; imagePath: string }>(
'http://localhost:4000/api/posts/' + id
);
}
addPost(title: string, content: string, image: File) {
const postData = new FormData();
postData.append('title', title);
postData.append('content', content);
postData.append('image', image, image.name); // Use image.name instead of title
this.http
.post<{ message: string; post: { id: string; title: string; content: string; imagePath: string } }>(
'http://localhost:4000/api/posts',
postData
)
.subscribe((responseData) => {
const post: Post = {
id: responseData.post.id,
title: title,
content: content,
imagePath: responseData.post.imagePath
};
this.posts.push(post);
this.postsUpdated.next([...this.posts]);
});
}
updatePost(id: string, title: string, content: string, image: File | string) {
let postData: FormData | Post;
if (typeof image === 'object') {
// If updating with new file
postData = new FormData();
postData.append('id', id);
postData.append('title', title);
postData.append('content', content);
postData.append('image', image, image.name);
} else {
// If updating without changing file
postData = {
id: id,
title: title,
content: content,
imagePath: image
};
}
this.http
.put('http://localhost:4000/api/posts/' + id, postData)
.subscribe(response => {
const updatedPosts = [...this.posts];
const oldPostIndex = updatedPosts.findIndex(p => p.id === id);
const post: Post = {
id: id,
title: title,
content: content,
imagePath: typeof image === 'string' ? image : ''
};
updatedPosts[oldPostIndex] = 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
interface Post {
id: string;
title: string;
content: string;
imagePath: string;
}
在整个服务中添加了图像路径处理,以正确跟踪上传的图像
Post 界面已更新(您的 Post 模型也应该更新):
为 HTTP 响应添加了正确的输入方式
在 updatePost 方法中添加了对图像更新的支持
您还需要更新后端路由才能正确处理图像:
router.post(
"",
multer({ storage: storage }).single("image"),
(req, res, next) => {
const url = req.protocol + '://' + req.get("host");
const post = new Post({
title: req.body.title,
content: req.body.content,
imagePath: url + "/images/" + req.file.filename
});
post.save().then(createdPost => {
res.status(201).json({
message: "Post added successfully",
post: {
id: createdPost._id,
title: createdPost.title,
content: createdPost.content,
imagePath: createdPost.imagePath
}
});
});
}
);
您的 Post mongoose 模型应包含 imagePath 字段:
const postSchema = mongoose.Schema({
title: { type: String, required: true },
content: { type: String, required: true },
imagePath: { type: String, required: true }
});
这些更改应该可以解决类型错误和图像上传问题。尝试一下看看。