I'm following a tutorial for image upload in Angular, but I get this error in my Chrome dev console whenever I post the form
TypeError: Argument 2 ('blobValue') to FormData.append must be an instance of Blob
here is my code for the routing to the database(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;
and this is the code for the post-service
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]);
});
}
}```
The error you encountered shows there’s a problem with the way the image file is being handled in the FormData. I made few changes to the code as shown below:
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]);
});
}
}
Changes I’ve made are:
How the image is appended to FormData
interface Post {
id: string;
title: string;
content: string;
imagePath: string;
}
Added image path handling throughout the service to properly track uploaded images
The Post interface was updated (your Post model should be updated too):
Added proper typing for HTTP responses
Added support for image updates in the updatePost method
You need to also update the backend route to handle the image properly:
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
}
});
});
}
);
Your Post mongoose model should include the imagePath field:
const postSchema = mongoose.Schema({
title: { type: String, required: true },
content: { type: String, required: true },
imagePath: { type: String, required: true }
});
These changes should resolve the TypeError and image upload issue. Try them and see.