Se han hecho preguntas similares, pero después de revisar todas esas y muchas publicaciones de blog sobre el tema, no he podido resolver esto, así que perdóneme.
Estoy creando un blog simple con (para los propósitos de esta pregunta) dos partes, un SPA de front-end en Angular 8 y una API de back-end en ASP.NET Core 3. En una parte de mi front-end, estoy intentando cargar una imagen que se utilizará como imagen para un blog recién creado. Cuando intento cargar una imagen, el archivo IFormFile resultante en el backend siempre sale a null. A continuación se muestra el código, ¡cualquier ayuda es muy apreciada!
new-blog.component.html:
<form [formGroup]="newBlogForm" (ngSubmit)="onSubmit(newBlogForm.value)">
<div>
<label for="Name">
Blog Name
</label>
<input type="text" formControlName="Name">
</div>
<div>
<label for="TileImage">
Tile Image
</label>
<input type="file" formControlName="TileImage">
</div>
<button type="submit">Create Blog</button>
</form>
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 de 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();
}
La firma de mi BlogController se ve así:
[HttpPost]
public async Task<ActionResult<Blog>> PostBlog([FromForm]PostBlogModel blogModel)
con el PostBlogModel de la siguiente manera:
public class PostBlogModel
{
public string Name { get; set; }
public IFormFile TileImage { get; set; }
}
Implementé un middleware de registro para intentar depurarlo. El resultado es el siguiente (veo que, por alguna razón, el front-end envía application / json en lugar de multipart / form-data, pero no estoy seguro de por qué o cómo solucionarlo ...)
blogapi_1 | info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
blogapi_1 | Request finished in 170.16740000000001ms 500
blogapi_1 | info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
blogapi_1 | Request starting HTTP/1.1 OPTIONS http://localhost:5432/api/v1/Blogs
blogapi_1 | dbug: BlogAPI.Middleware.RequestResponseLoggingMiddleware[0]
blogapi_1 | HTTP Request: Headers:
blogapi_1 | key: Connection, values: keep-alive
blogapi_1 | key: Accept, values: */*
blogapi_1 | key: Accept-Encoding, values: gzip, deflate, br
blogapi_1 | key: Accept-Language, values: en-US,en-IN;q=0.9,en;q=0.8,en-GB;q=0.7
blogapi_1 | key: Host, values: localhost:5432
blogapi_1 | key: Referer, values: http://localhost:5431/blog/new-blog
blogapi_1 | key: User-Agent, values: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
blogapi_1 | key: Origin, values: http://localhost:5431
blogapi_1 | key: Access-Control-Request-Method, values: POST
blogapi_1 | key: Access-Control-Request-Headers, values: content-type
blogapi_1 | key: Sec-Fetch-Site, values: same-site
blogapi_1 | key: Sec-Fetch-Mode, values: cors
blogapi_1 |
blogapi_1 | type:
blogapi_1 | scheme: http
blogapi_1 | host+path: localhost:5432/api/v1/Blogs
blogapi_1 | queryString:
blogapi_1 | body:
blogapi_1 | info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
blogapi_1 | CORS policy execution successful.
blogapi_1 | dbug: BlogAPI.Middleware.RequestResponseLoggingMiddleware[0]
blogapi_1 | HTTP Response: Headers:
blogapi_1 | key: Access-Control-Allow-Headers, values: Content-Type
blogapi_1 | key: Access-Control-Allow-Origin, values: http://localhost:5431
blogapi_1 |
blogapi_1 | statusCode: 204
blogapi_1 | responseBody:
blogapi_1 | info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
blogapi_1 | Request finished in 58.5088ms 204
blogapi_1 | info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
blogapi_1 | Request starting HTTP/1.1 POST http://localhost:5432/api/v1/Blogs application/json 56
blogapi_1 | dbug: BlogAPI.Middleware.RequestResponseLoggingMiddleware[0]
blogapi_1 | HTTP Request: Headers:
blogapi_1 | key: Connection, values: keep-alive
blogapi_1 | key: Content-Type, values: application/json
blogapi_1 | key: Accept, values: application/json, text/plain, */*
blogapi_1 | key: Accept-Encoding, values: gzip, deflate, br
blogapi_1 | key: Accept-Language, values: en-US,en-IN;q=0.9,en;q=0.8,en-GB;q=0.7
blogapi_1 | key: Host, values: localhost:5432
blogapi_1 | key: Referer, values: http://localhost:5431/blog/new-blog
blogapi_1 | key: User-Agent, values: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
blogapi_1 | key: Origin, values: http://localhost:5431
blogapi_1 | key: Content-Length, values: 56
blogapi_1 | key: Sec-Fetch-Site, values: same-site
blogapi_1 | key: Sec-Fetch-Mode, values: cors
blogapi_1 |
blogapi_1 | type: application/json
blogapi_1 | scheme: http
blogapi_1 | host+path: localhost:5432/api/v1/Blogs
blogapi_1 | queryString:
blogapi_1 | body: {"Name":"test","TileImage":"C:\\fakepath\\DSC_0327.jpg"}
mi BlogController se ve así:
[HttpPost] public async Task<ActionResult<Blog>> PostBlog([FromForm]PostBlogModel blogModel)
Parece que le gustaría pasar datos usando form-data, para lograrlo, puede consultar el siguiente ejemplo de código.
.component.html
<form [formGroup]="newBlogForm" (ngSubmit)="onSubmit(newBlogForm.value)">
<div>
<label for="Name">
Blog Name
</label>
<input type="text" formControlName="Name">
</div>
<div>
<label for="TileImage">
Tile Image
</label>
<input type="file" formControlName="TileImage" (change)="onSelectFile($event)" >
</div>
<button type="submit">Create Blog</button>
</form>
.componente.ts
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();
}
Resultado de la prueba

primero
<input type="file">vinculante con uso angular ngModelo formControlNamesolo capturará el, value propertypero en realidad cuando enviamos el formulario lo necesitamos files propertypara poder crear una directiva personalizada que se aplicará a todos los <input type="file">elementos del proyecto , por lo que cuando enviamos el formulario obtenemos la propiedad del archivo
antes de

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]);
}
}
Después
Segundo
Para cargar un archivo usando Http, sus datos deben codificarse usando multipart/form-dataque permita que los archivos se envíen a través de la publicación http, por eso FormDatase usa,
Un objeto FormData generará automáticamente datos de solicitud con datos de formulario / multiparte tipo MIME que los servidores existentes pueden procesar. Para agregar un campo de archivo a los datos, usa un objeto Archivo que una extensión puede construir a partir de la ruta del archivo. El objeto FormData simplemente se puede pasar a XMLHttpRequest:
Cargar archivos Http
por lo que su método de envío debería ser como
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
}
Tercero
En su postBlogmétodo que está creando Subjectsin ningún beneficio, puede simplemente devolver http.post y luego en el método de llamada use especificar si usted subscribeo usa async/awaitpara hacer que la llamada http se active
onSubmit() {
.....
this.postBlog(formData).subscribe(
result => { }
);
}
async onSubmit() {
.....
let res = await this.postBlog(formData).toPromise();
}
Jana Duggar ha sido abierta sobre su búsqueda del amor. Aquí está todo lo que ha dicho sobre el tema y su ventana de cinco años para el matrimonio.
La estrella de 'Outlander', Sam Heughan, reveló recientemente qué se llevará del set para recordar a Jamie Fraser, el papel que lo convirtió en una estrella.
'She Loves You' fue escrita en una hora, grabada en un día y fue la canción de The Beatles con una de las mejores interpretaciones de su carrera.
Dolly Parton y su abuela Bessie tenían una relación especial. La abuela Parton estaba muy enferma, pero eso no impidió que Dolly le hiciera una broma.
Puede que pienses que el roble o el nogal americano son maderas resistentes, pero cuando se trata de las maderas más duras del mundo, ni siquiera se acercan.
El océano rebosa belleza, pero también esconde algunas de las criaturas marinas más aterradoras del planeta. Muchos de estos animales acechan en las profundidades oceánicas, en el oscuro y opresivo mundo de alta presión del océano.
Si te enfrentas a monstruos hostiles o te preparas para combates PvP, conocer los mejores encantamientos para espadas en Minecraft puede darte una gran ventaja. Encantar espadas te permite infligir más daño, aumentar los objetos que sueltan los monstruos y prolongar la durabilidad de tu espada.
Cuando se habla de países socialistas, a menudo se imagina a la gente con un control estatal total y sin propiedad privada. Pero en la práctica, las economías socialistas varían enormemente.
“Children” lanza un cameo de renombre y algunas noticias devastadoras
Se dice que este RAV4 está en excelentes condiciones y preparado para un juego de sillas musicales.
Imágenes de drones capturaron a los bomberos que parecían tener dificultades para apagar el incendio.
Eyes of Wakanda se vincula directamente con la MCU, además de actualizaciones sobre X-Men '97, What If..., Daredevil y más.
El anillo de compromiso de Kate Middleton perteneció a la princesa Diana antes de que el príncipe William le propusiera matrimonio con él. Descubra todo lo que hay que saber sobre esta pieza histórica aquí.
John Cleese se casó con su esposa, Jennifer Wade, en 2012. Aquí encontrará todo lo que debe saber sobre la esposa de John Cleese, Jennifer Wade.
Patton Oswalt se casó con su esposa, la también actriz Meredith Salenger, en 2017. Aquí encontrará todo lo que debe saber sobre la esposa de Patton Oswalt, Meredith Salenger.
Aquí encontrará todo lo que debe saber sobre Lauren Scruggs Kennedy, la bloguera de moda y autora de best sellers del New York Times que espera su segundo hijo con la personalidad de televisión Jason Kennedy.
Me llama la atención De repente, como ocurre con tanta frecuencia Es el punto medio de nuevo <No, no el del Pacífico, tú, el del carnaval> Todos los titulares en voz alta...
En 2022, con el lanzamiento de GPT-3, la habilidad de escritura parece perder importancia. Con IA, las personas con mala escritura también pueden generar personal de calidad.
En el mundo acelerado y competitivo de hoy, la carrera de uno juega un papel importante en el crecimiento personal, la estabilidad financiera y la satisfacción general con la vida. Sin embargo, muchas personas se encuentran atrapadas en un ciclo implacable de estancamiento e insatisfacción, y su vida profesional se desvanece gradualmente.
La semana pasada noté un comunicado de prensa, enviado a través de PressGazette (un sitio web de noticias de los medios británicos). El artículo anunciaba que Acast, la empresa de publicidad y alojamiento de podcasts de Scandi, lideraría un consorcio de editores "probablemente los más influyentes" en podcasting.