PostCreateComponent.html:56错误TypeError:对'FormData'执行'append'失败:参数2不是'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)
不知道为什么会发生错误真令人沮丧,因为在MDN上,它清楚地指出,当您添加表单数据时,可选输入是文件名,
来源:MDN
此方法有两个版本:两个和三个参数版本:
formData.append(name,value); formData.append(名称,值,文件名); ParametersSection名称。数据包含在值中的字段的名称。字段的值。这可以是USVString或Blob(包括子类,例如File)。当将Blob或File作为第二个参数传递时,报告给服务器的文件名(USVString)。 Blob对象的默认文件名是“ blob”。 File对象的默认文件名是文件的文件名。
请参阅下面的代码,重点关注posts.service.ts第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>
答案 0 :(得分:1)
问题似乎是上传的文件未转换为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 });
};
}
答案 1 :(得分:0)
也许与您的问题完全无关,但是如果它可能对其他人有帮助,在尝试使用ant design上传多个文件时,我会使用 React 遇到相同的问题。我的问题是我正在使用这个:
const formData = new FormData()
formData.append(
"file",
file[0],
file[0].name
);
将文件[0]作为对象
代替此:
const formData = new FormData()
formData.append(
"file",
file[0].originFileObj,
file[0].originFileObj.name
);
哪个给我一个文件。