C# 如何将文件从Angular上载到ASP.NET核心Web API
有人问过类似的问题,但在浏览了所有这些和许多关于这个主题的博客文章后,我还没有弄明白这一点,所以请原谅我 为了回答这个问题,我正在创建一个简单的博客,其中包含两个部分,Angular 8中的前端SPA和ASP.NET Core 3中的后端API。在我的前端的一部分,我试图上传一个图像作为新创建的博客的图像。当我尝试上载图像时,后端生成的文件总是显示为C# 如何将文件从Angular上载到ASP.NET核心Web API,c#,angular,typescript,asp.net-core,angular8,C#,Angular,Typescript,Asp.net Core,Angular8,有人问过类似的问题,但在浏览了所有这些和许多关于这个主题的博客文章后,我还没有弄明白这一点,所以请原谅我 为了回答这个问题,我正在创建一个简单的博客,其中包含两个部分,Angular 8中的前端SPA和ASP.NET Core 3中的后端API。在我的前端的一部分,我试图上传一个图像作为新创建的博客的图像。当我尝试上载图像时,后端生成的文件总是显示为null。下面是代码,非常感谢您的帮助 new-blog.component.html: 博客名 瓷砖图像 创建博客 new-blog.comp
null
。下面是代码,非常感谢您的帮助
new-blog.component.html:
博客名
瓷砖图像
创建博客
new-blog.component.ts:
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormControl } from '@angular/forms';
import { BlogService } from '../blog-services/blog.service';
@Component({
selector: 'app-new-blog',
templateUrl: './new-blog.component.html',
styleUrls: ['./new-blog.component.css']
})
export class NewBlogComponent implements OnInit {
private newBlogForm: FormGroup;
constructor(private formBuilder: FormBuilder, private blogService: BlogService) { }
ngOnInit() {
this.newBlogForm = this.formBuilder.group({
Name: new FormControl(null),
TileImage: new FormControl(null)
});
}
onSubmit(blogData: FormData) {
console.log('new blog has been submitted.', blogData);
this.blogService.postBlog(blogData);
this.newBlogForm.reset();
}
}
postBlog
来自blog.service.ts:
postBlog(blogData: FormData): Observable<any> {
const postBlogSubject = new Subject();
this.appOptions.subscribe(
(options) => {
const url = options.blogAPIUrl + '/Blogs';
this.http
.post(url, blogData)
.subscribe(
(blog) => {
postBlogSubject.next(blog);
}
);
}
);
return postBlogSubject.asObservable();
}
我已经实现了日志中间件来尝试调试。输出如下(我看到前端出于某种原因发送application/json而不是multipart/form数据,但我不确定为什么或如何修复…)
我的博客控制器如下所示:
[HttpPost]
public async Task<ActionResult<Blog>> PostBlog([FromForm]PostBlogModel blogModel)
[HttpPost]
公共异步任务首先
使用ngModel
或formControlName
绑定angular只会捕获值属性
但实际上,当我们提交表单时,我们需要文件属性
,以便创建
适用于所有项目
元素的自定义指令,因此
我们提交了我们获得文件属性的表单
以前
之后
秒
要使用Http上传文件,您的数据应使用允许通过Http post发送文件的multipart/form data
进行编码,以便使用FormData
FormData对象将使用MIME自动生成请求数据
键入现有服务器可以处理的多部分/表单数据。添加
“文件”字段,以显示您使用的扩展名可以访问的文件对象的数据
从文件路径构造。然后可以简单地创建FormData对象
传递到XMLHttpRequest:
所以你的提交方法应该是
onSubmit() {
let formData: FormData = new FormData();
Object.keys(this.newBlogForm.value).forEach(key => {
formData.append(key, this.newBlogForm.value[key])
});
//pass formData to your service
}
第三次
在您创建的postBlog
方法中,您创建的Subject
没有任何好处,您只需返回http.post,然后在调用方方法中使用指定的subscribe
或async/wait
来启动http调用
onSubmit() {
.....
this.postBlog(formData).subscribe(
result => { }
);
}
async onSubmit() {
.....
let res = await this.postBlog(formData).toPromise();
}
您是否尝试将包含内容类型标头的httpOptions添加到http.post?似乎您正在使用跨域服务。您的浏览器将首先发送一个HTTP选项请求,并测试返回的头是否允许它进行真正的提交。在HTTP选项测试过程中,服务器无法获取该文件。请参阅:@Yeheshuah-当我尝试添加内容类型标题时,ASP.NET core似乎无法处理输入,它仍然从角度显示正文,带有{“Name”:“test”,“TileImage”:“C:\\fakepath\\DSC\u 0327.jpg”}
,我得到错误System.IO.InvalidDataException:缺少内容类型边界。
@Anduin-是,这是在Docker容器中运行的,但是,正如日志显示的那样,选项请求似乎正在成功执行,并且在响应中返回了访问控制允许源代码,因为我已经为此设置了CORS策略。您是如何在post请求中设置HttpOptions的?@AngelaAmarapala我在上面的postBlog函数中添加了以下内容:let HttpOptions:any={headers:newhttpheaders({“内容类型”:“multipart/formdata”});this.http.post(url、blogData、httpOptions)
。可能还需要补充一点,当我指定此选项时,似乎没有提出选项请求,它只是直接发送到帖子。我是angular方面的新手,你能解释一下为什么没有PostBlog和.subscribe中的异步任务,它就不能工作吗(res=>{alert('upload!!');});它不会引发异常,但不会进入方法。是否必须将FormData对象单独发送到后端?我似乎无法将FormData对象包装到另一个对象中,然后将该对象一起发送
selectedFile: File = null;
private newBlogForm: FormGroup;
constructor(private http: HttpClient) { }
ngOnInit() {
this.newBlogForm = new FormGroup({
Name: new FormControl(null),
TileImage: new FormControl(null)
});
}
onSelectFile(fileInput: any) {
this.selectedFile = <File>fileInput.target.files[0];
}
onSubmit(data) {
const formData = new FormData();
formData.append('Name', data.Name);
formData.append('TileImage', this.selectedFile);
this.http.post('your_url_here', formData)
.subscribe(res => {
alert('Uploaded!!');
});
this.newBlogForm.reset();
}
import { Directive, forwardRef, HostListener, ElementRef, Renderer2 } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
@Directive({
selector : `input[type=file][formControlName],
input[type=file][formControl],
input[type=file][ngModel]`,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: FileValueAccessorDirective,
multi: true
}
]
})
export class FileValueAccessorDirective implements ControlValueAccessor {
constructor(private elementRef: ElementRef, private render: Renderer2) {
}
// Function to call when the file changes.
onChange = (file: any) => {}
//fire when the form value changed programmaticly
writeValue(value: any): void {
}
//fire only one time to register on change event
registerOnChange = (fn: any) => { this.onChange = fn; }
//fire only one time to register on touched event
registerOnTouched = (fn: any) => { }
//Disable the input
setDisabledState?(isDisabled: boolean): void {
}
//listen to change event
@HostListener('change', ['$event.target.files'])
handleChange(file) {
this.onChange(file[0]);
}
}
onSubmit() {
let formData: FormData = new FormData();
Object.keys(this.newBlogForm.value).forEach(key => {
formData.append(key, this.newBlogForm.value[key])
});
//pass formData to your service
}
onSubmit() {
.....
this.postBlog(formData).subscribe(
result => { }
);
}
async onSubmit() {
.....
let res = await this.postBlog(formData).toPromise();
}