Загрузка файла из Angular в ASP.NET Core

Dec 11 2020

Впервые я пытаюсь загрузить файл из компонента Angular на веб-страницу ASPNET Core и просто не могу заставить его работать. Надеюсь, следующих фрагментов кода будет достаточно, чтобы показать основную часть того, что происходит. Проблема в том, что хотя я подтверждаю, что переданный параметр методу post HttpClient (frmData) действителен, метод действия ASPNet Core никогда его не видит и сообщает, что IFormFile всегда имеет значение null.

РЕДАКТИРОВАТЬ: я ранее пробовал использовать multipart / form-data в качестве типа содержимого, но я дал необработанное исключение в кишках Kestrel. Теперь я понимаю, что это правильный способ сделать это, и использование типа содержимого json было источником моей ОРИГИНАЛЬНОЙ проблемы. Но я не знаю, что делать дальше. Я вижу из некоторых поисковых запросов, что существует около \ миллиарда различных причин возникновения этого исключения.

POST Выполняющая конечная точка 'JovenesA.Controllers.StudentssController.PostStudentGradesReport (JAWebAPI)'
04: 55: 38.4853 Информация ControllerActionInvoker
Маршрут POST соответствует {action = "PostStudentGradesReport", controller = "Becas"}. Выполнение действия JovenesA.Controllers.BecasController.PostStudentGradesReport (JAWebAPI)
04: 55: 38.5032 Ошибка DeveloperExceptionPageMiddleware
POST При выполнении запроса возникло необработанное исключение.
04: 55: 38.5333 Информация WebHost
POST-запрос завершился за 48,1225 мс 500 текст / HTML; charset = utf-8
04: 55: 38.5333 Информация Пустельга
 Идентификатор соединения «0HM4UHGE85O17», идентификатор запроса «0HM4UHGE85O17: 00000006»: приложение завершено без чтения всего тела запроса.

Любая помощь будет принята с благодарностью!

Угловой компонент:

fileEntry.file((file: File) => {
      console.log('fileEntry relativePath: ' + currFile.relativePath);
      console.log('filEntry.name: ', file.name);
      console.log('filEntry.size: ', file.size);

      const frmData = new FormData();
      frmData.append(file.name, file);

      this.studentData.uploadStudentGradesReport(file.name, frmData).subscribe(
        () => {
          this.successMessage = 'Changes were saved successfully.';
          window.scrollTo(0, 0);
          window.setTimeout(() => {
            this.successMessage = '';
          }, 3000);
        },
        (error) => {
          this.errorMessage = error;
        }
      );
    });

Угловая служба:

public uploadStudentGradesReport(filename: string, frmData: FormData): Observable<any> {
    const url = this.WebApiPrefix + 'students/' + 'student-grades-report';
    const headers = new HttpHeaders().set('Content-Type', 'application/json');
    if (frmData) {
      console.log('ready to post ' + url + ' filename: ' + filename + ' options ' + headers);
      return this.http.post(url, frmData, { headers });
    }
}

Элемент управления ASPNET Core

// POST api/students/student-grades-report
[HttpPost("student-grades-report", Name = "PostStudentGradseReportRoute")]
//[ValidateAntiForgeryToken]
[ProducesResponseType(typeof(GradesGivenEntryApiResponse), 200)]
[ProducesResponseType(typeof(GradesGivenEntryApiResponse), 400)]
public async Task<ActionResult> PostStudentGradesReport([FromForm] IFormFile myFile)
{
    _Logger.LogInformation("Post StudentGradesReport  ");

    if (myFile != null)
    {
        var totalSize = myFile.Length;
        var fileBytes = new byte[myFile.Length];

Если это поможет, вот данные, которые отправляются в запросе POST.

ЗАПИСЬ http://192.168.0.16:1099/api/students/student-grades-report HTTP / 1.1
Хост: 192.168.0.16:1099
Подключение: keep-alive
Длина содержимого: 13561
Принять: application / json, text / plain, * / *
DNT: 1
Пользовательский агент: Mozilla / 5.0 (Windows NT 10.0; Win64; x64) AppleWebKit / 537.36 (KHTML, как Gecko) Chrome / 87.0.4280.88 Safari / 537.36
Тип содержимого: приложение / json
Происхождение: http: // localhost: 3000
Ссылка: http: // localhost: 3000 /
Принятие кодировки: gzip, deflate
Accept-Language: en-US, en; q = 0,9, es-MX; q = 0,8, es; q = 0,7

------ WebKitFormBoundaryBVuZ7IbkjtQAKQ0a
Content-Disposition: форма-данные; name = "test1.PNG"; filename = "test1.PNG"
Тип содержимого: изображение / png

 PNG
 
  [двоичное содержимое файла изображения]  

------ WebKitFormBoundaryBVuZ7IbkjtQAKQ0a--

Ответы

2 jandrew Dec 12 2020 at 09:30

Вы отправляете файл как данные формы, поэтому вам необходимо указать правильный заголовок типа содержимого. В настоящее время ваша отправка application/jsonв Content-Typeзаголовке. Это верно даже при вызове API, что, по понятным причинам, поначалу может сбивать с толку. Правильный тип контента в этом случае - multipart/form-data. Ваш API не видит, IFormFileпотому что считает, что запрос - это JSON. Я изменил ваш код Angular, указав правильное значение заголовка типа содержимого.

Изменить: оказывается, что указание Content-Typeзаголовка вручную приведет к тому, что граничные значения не будут автоматически установлены в значении заголовка. Вместо этого простое решение - не добавлять заголовок самостоятельно, что приведет к автоматической установке надлежащего типа содержимого и граничных значений. Если вы установите заголовок самостоятельно, вам также придется установить граничные значения. В большинстве случаев лучшим решением будет оставить значение по умолчанию. Ссылка на вопрос / ответ, указывающий на это. FormData, как получить или установить границу в multipart / form-data - Angular

public uploadStudentGradesReport(filename: string, frmData: FormData): Observable<any> {
    const url = this.WebApiPrefix + 'students/' + 'student-grades-report';
    const headers = new HttpHeaders().set('Content-Type', 'multipart/form-data');
    if (frmData) {
      console.log('ready to post ' + url + ' filename: ' + filename + ' options ' + headers);
      return this.http.post(url, frmData, { headers });
    }
}

Вы также можете отметить расположение содержимого в предоставленном вами HTTP-запросе, которое показывает данные формы вместе с типом прикрепленного файла. Надеюсь это поможет. Я не запускал проект Angular для тестирования вашего кода, но тип содержимого должен исправить вашу проблему.

Изменить : я заметил, что вы используете имя файла в качестве ключа для поля формы с файлом. Вам нужно использовать такой ключ, как просто «файл» для поля формы, который должен соответствовать имени параметра в вашем коде контроллера. Вы можете получить фактическое имя файла в коде вашего контроллера, ключ просто указывает, к какому полю формы прикреплены файлы. пример

frmData.append('file', file);

А затем для действия вашего контроллера

public async Task<IActionResult> PostStudentGradesReport([FromForm] IFormFile file)
{
    if (file.Length <= 0 || file.ContentType is null) return BadRequest();
    var actualFileName = file.FileName;

    using (var stream = file.OpenReadStream())
    {
        // Process file...
    }
    
    return Ok(); 
}
1 Coco Dec 11 2020 at 23:02

Я не могу гарантировать, что это сработает, но вы можете попробовать использовать Angular HttpRequest. Итак, в вашем сервисе angular попробуйте следующее:

const request = new HttpRequest (
    'POST',
     url, // http://localhost/your_endpoint
     frmData,
     { withCredentials: false }
);
    
return this.http.request(request);

Также обратите внимание, что вам не следует выполнять проверку данных в функции, вызывающей backend Api. Что ваша функция вернет, если if(frmData)она ложна?