PostCreateComponent.html:56 ERROR TypeError: Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'.
at PostsService.push../src/app/posts/posts.service.ts.PostsService.addPost (posts.service.ts:99)
at PostCreateComponent.push../src/app/posts/post-create/post-create.component.ts.PostCreateComponent.onSavePost (post-create.component.ts:165)
it is so frustrating not knowing why the error is happening because on MDN, it clearly state, when you append a form data, the optional input is the file name,
Source: MDN
There are two versions of this method: a two and a three parameter version:
formData.append(name, value); formData.append(name, value, filename); ParametersSection name. The name of the field whose data is contained in the value.The field's value. This can be a USVString or Blob (including subclasses such as File). The filename reported to the server (a USVString), when a Blob or File is passed as the second parameter. The default filename for Blob objects is "blob". The default filename for File objects is the file's filename.
Please see code below focus on posts.service.ts line 99
post.create.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { Subscription } from 'rxjs';
import { PostsService } from '../posts.service';
import { Post } from '../post.model';
import { mimeType } from './mime-type.validator';
import { AuthService } from 'src/app/auth/auth.service';
export interface CATEGORY {
value: string;
viewValue: string;
}
export interface DURATION {
value: string;
viewValue: string;
}
@Component({
selector: 'app-post-create',
templateUrl: './post-create.component.html',
styleUrls: ['./post-create.component.css']
})
export class PostCreateComponent implements OnInit, OnDestroy {
types: CATEGORY[] = [
{value: 'Feature Film', viewValue: 'Feature Film'},
{value: 'Short Film', viewValue: 'Short Film'},
{value: 'TV Series', viewValue: 'TV Series'}
];
contracts: DURATION[] = [
{value: '3 Month', viewValue: '3 Month'},
{value: '6 Month', viewValue: '6 Month'},
{value: '9 Month', viewValue: '9 Month'},
{value: '12 Month', viewValue: '12 Month'},
];
enteredName = '';
enteredContent = '';
enteredGenre = '';
enteredAuthor = '';
enteredDuration = '';
enteredYear = '';
enteredCategory = '';
enteredContractDuration = '';
post: Post;
isLoading = false;
isLinear = false;
form: FormGroup;
imagePreview: string;
private mode = 'create';
private postId: string;
private authStatusSub: Subscription;
constructor(
public postsService: PostsService,
public route: ActivatedRoute,
private authService: AuthService
) {}
ngOnInit() {
this.authStatusSub = this.authService
.getAuthStatusListener()
.subscribe(authStatus => {
this.isLoading = false;
});
this.form = new FormGroup({
name: new FormControl(null, {
validators: [Validators.required, Validators.minLength(3)]
}),
content: new FormControl(null, { validators: [Validators.required] }),
image: new FormControl(null, {
validators: [Validators.required],
asyncValidators: [mimeType]
}),
genre: new FormControl(null, {
validators: [Validators.required, Validators.minLength(3)]
}),
author: new FormControl(null, {
validators: [Validators.required, Validators.minLength(3)]
}),
duration: new FormControl(null, {
validators: [Validators.required, Validators.minLength(3)]
}),
year: new FormControl(null, {
validators: [Validators.required, Validators.minLength(3)]
}),
category: new FormControl(null, {
validators: [Validators.required, Validators.minLength(3)]
}),
contractDuration: new FormControl(null, {
validators: [Validators.required, Validators.minLength(3)]
})
});
this.route.paramMap.subscribe((paramMap: ParamMap) => {
if (paramMap.has('postId')) {
this.mode = 'edit';
this.postId = paramMap.get('postId');
this.isLoading = true;
this.postsService.getPost(this.postId).subscribe(postData => {
this.isLoading = false;
this.post = {
id: postData._id,
name: postData.name,
genre: postData.genre,
author: postData.author,
duration: postData.duration,
year: postData.year,
category: postData.category,
content: postData.content,
imagePath: postData.imagePath,
creator: postData.creator,
adminApproval: postData.adminApproval,
isApproved: postData.isApproved,
createdAt: postData.createdAt,
contractDuration: postData.contractDuration
};
this.form.setValue({
name: this.post.name,
content: this.post.content,
image: this.post.imagePath,
genre: this.post.genre,
author: this.post.author,
duration: this.post.duration,
year: this.post.year,
category: this.post.category,
contractDuration: this.post.contractDuration
});
});
} else {
this.mode = 'create';
this.postId = null;
}
});
}
onImagePicked(event: Event) {
const file = (event.target as HTMLInputElement).files[0];
this.form.patchValue({ image: file });
this.form.get('image').updateValueAndValidity();
const reader = new FileReader();
reader.onload = () => {
this.imagePreview = <string>reader.result;
};
reader.readAsDataURL(file);
}
onSavePost() {
if (this.form.invalid) {
return;
}
this.isLoading = true;
if (this.mode === 'create') {
console.log(this.form.value.name,
this.form.value.content,
this.form.value.image,
this.form.value.genre,
this.form.value.author,
this.form.value.duration,
this.form.value.year,
this.form.value.category,
this.form.value.contractDuration);
this.postsService.addPost(
this.form.value.name,
this.form.value.content,
this.form.value.image,
this.form.value.genre,
this.form.value.author,
this.form.value.duration,
this.form.value.year,
this.form.value.category,
this.form.value.contractDuration
);
} else {
this.postsService.updatePost(
this.postId,
this.form.value.name,
this.form.value.content,
this.form.value.image,
this.form.value.genre,
this.form.value.author,
this.form.value.duration,
this.form.value.year,
this.form.value.category,
this.form.value.contractDuration
);
}
this.form.reset();
}
ngOnDestroy() {
this.authStatusSub.unsubscribe();
}
}
post.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Subject } from 'rxjs';
import { map } from 'rxjs/operators';
import { Router } from '@angular/router';
import { Post } from './post.model';
@Injectable({ providedIn: 'root' })
export class PostsService {
private posts: Post[] = [];
private postsUpdated = new Subject<{ posts: Post[]; postCount: number }>();
constructor(private http: HttpClient, private router: Router) {}
getPosts(postsPerPage: number, currentPage: number) {
const queryParams = `?pagesize=${postsPerPage}&page=${currentPage}`;
this.http
.get<{ message: string; posts: any; maxPosts: number }>(
'http://localhost:3000/api/posts' + queryParams
)
.pipe(
map(postData => {
return {
posts: postData.posts.map(post => {
return {
name: post.name,
genre: post.genre,
author: post.author,
duration: post.duration,
year: post.year,
category: post.category,
content: post.content,
id: post._id,
imagePath: post.imagePath,
creator: post.creator,
adminApproval: post.adminApproval,
isApproved: post.isApproved,
createdAt: post.createdAt,
contractDuration: post.contractDuration
};
}),
maxPosts: postData.maxPosts
};
})
)
.subscribe(transformedPostData => {
console.log(transformedPostData);
this.posts = transformedPostData.posts;
this.postsUpdated.next({
posts: [...this.posts],
postCount: transformedPostData.maxPosts
});
});
}
getPostUpdateListener() {
return this.postsUpdated.asObservable();
}
getPost(id: string) {
return this.http.get<{
_id: string;
name: string;
genre: string;
author: string;
duration: string;
year: string;
category: string;
content: string;
imagePath: string;
creator: string;
adminApproval: boolean;
isApproved: boolean;
createdAt: Date;
contractDuration: string;
}>('http://localhost:3000/api/posts/' + id);
}
addPost(
name: string,
genre: string,
author: string,
duration: string,
year: string,
category: string,
content: string,
contractDuration: string,
image: File) {
const postData = new FormData();
postData.append('name', name);
postData.append('genre', genre);
postData.append('author', author);
postData.append('duration', duration);
postData.append('year', year);
postData.append('category', category);
postData.append('content', content);
postData.append('contractDuration', contractDuration);
postData.append('image', image, name);
this.http
.post<{ message: string; post: Post }>(
'http://localhost:3000/api/posts',
postData
)
.subscribe(responseData => {
this.router.navigate(['/programs']);
});
}
updatePost(
id: string,
name: string,
genre: string,
author: string,
duration: string,
year: string,
category: string,
content: string,
contractDuration: string,
image: File | string) {
let postData: Post | FormData;
if (typeof image === 'object') {
postData = new FormData();
postData.append('id', id);
postData.append('name', name);
postData.append('genre', genre);
postData.append('author', author);
postData.append('duration', duration);
postData.append('year', year);
postData.append('category', category);
postData.append('contractDuration', contractDuration);
postData.append('content', content);
postData.append('image', image, name);
} else {
postData = {
id: id,
name: name,
genre: genre,
author: author,
duration: duration,
year: year,
category: category,
contractDuration: contractDuration,
content: content,
imagePath: image,
creator: null,
adminApproval: null,
isApproved: null,
createdAt: null
};
}
this.http
.put('http://localhost:3000/api/posts/' + id, postData)
.subscribe(response => {
this.router.navigate(['/programs']);
});
}
deletePost(postId: string) {
return this.http
.delete('http://localhost:3000/api/posts/' + postId);
}
}
post-create.component.html
<mat-card>
<mat-spinner *ngIf="isLoading"></mat-spinner>
<form [formGroup]="form" (submit)="onSavePost()" *ngIf="!isLoading">
<label class="ui-label" for="form_name">Title</label>
<mat-form-field>
<input matInput type="text" formControlName="name" placeholder="Teza">
<mat-error *ngIf="form.get('name').invalid">Please enter the films name.</mat-error>
</mat-form-field>
<label class="ui-label" for="form_vertical_preview">Horizontal poster</label>
<div class="ui-caption">
Recommended: PNG or JPG file @ 740x420 resolution.
</div>
<button mat-stroked-button type="button" (click)="filePicker.click()">Pick Image</button>
<div class="image-picker">
<input type="file" #filePicker (change)="onImagePicked($event)">
<div class="image-preview" *ngIf="imagePreview !== '' && imagePreview && form.get('image').valid">
<img [src]="imagePreview" [alt]="form.value.name">
</div>
</div>
<label class="ui-label" for="form_description">Description</label>
<mat-form-field>
<textarea matInput rows="4" formControlName="content" placeholder="Set in 1970s Ethiopia, Teza (Morning Dew) tells the story of a young Ethiopian as he returns from West Germany a postgraduate. Anberber comes back to a country at the height of the Cold War and under the Marxist regime of Mengistu Haile Mariam."></textarea>
<mat-error *ngIf="form.get('content').invalid">Please enter a description.</mat-error>
</mat-form-field>
<label class="ui-label" for="form_genre">Genre</label>
<mat-form-field>
<input matInput type="text" formControlName="genre" placeholder="Action, Adventure, Romance ....">
<mat-error *ngIf="form.get('genre').invalid">Please enter a program genre.</mat-error>
</mat-form-field>
<label class="ui-label" for="form_author">Author</label>
<mat-form-field>
<input matInput type="text" formControlName="author" placeholder="Haile Gerima, Zeresenay ...">
<mat-error *ngIf="form.get('author').invalid">Please enter an author.</mat-error>
</mat-form-field>
<label class="ui-label" for="form_duration">Duration</label>
<mat-form-field>
<input matInput type="text" formControlName="duration" placeholder="2h35m">
<mat-error *ngIf="form.get('duration').invalid">Please enter a duration.</mat-error>
</mat-form-field>
<label class="ui-label" for="form_year">Year</label>
<mat-form-field>
<input matInput type="text" formControlName="year" placeholder="2019">
<mat-error *ngIf="form.get('year').invalid">Please enter a year.</mat-error>
</mat-form-field>
<label class="ui-label" for="form_category">Category</label>
<mat-form-field>
<mat-select placeholder="shortfilm, feature film, tv series" formControlName="category">
<mat-option *ngFor="let type of types" [value]="type.value">
{{type.viewValue}}
</mat-option>
</mat-select>
<input matInput type="text" formControlName="category" placeholder="">
<mat-error *ngIf="form.get('category').invalid">Please enter a category.</mat-error>
</mat-form-field>
<mat-form-field>
<mat-select placeholder="3 Months, 6 Months ..." formControlName="contractDuration">
<mat-option *ngFor="let contract of contracts" [value]="contract.value">
{{contract.viewValue}}
</mat-option>
</mat-select>
<input matInput type="text" formControlName="contractDuration" placeholder="">
<mat-error *ngIf="form.get('contractDuration').invalid">Please enter a contract Duration.</mat-error>
</mat-form-field>
<button mat-raised-button color="accent" type="submit">Save Post</button>
</form>
</mat-card>
Perhaps completely unrelated to your question, but in case it may be helpful to someone else, I got the same issue using React when trying to upload multiple files using ant design. My issue was that I was using this:
const formData = new FormData()
formData.append(
"file",
file[0],
file[0].name
);
Which has file[0] as an object
Instead of this:
const formData = new FormData()
formData.append(
"file",
file[0].originFileObj,
file[0].originFileObj.name
);
Which gives me a file.
The issue appears to be that the uploaded file was not converted to a Blob.
onImagePicked(event: Event) {
const file = (event.target as HTMLInputElement).files[0];
this.form.patchValue({ image: file });
this.form.get('image').updateValueAndValidity();
const reader = new FileReader();
reader.onload = function(e) {
this.imagePreview = <string>reader.result;
// convert uploaded file to blob
const blob = new Blob([new Uint8Array(e.target.result)], {type: file.type });
};
}
Source: Convert data file to blob
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With