AngularからASP.NETCoreへのファイルのアップロード

Dec 11 2020

初めて、AngularコンポーネントからASPNET Core Webページにファイルをアップロードしようとしましたが、それを機能させることができません。うまくいけば、次のコードの抜粋は、起こっていることの本質を示すのに十分でしょう。問題は、HttpClientのpostメソッド(frmData)に渡されたパラメーターが有効であることを確認しても、ASPNet Coreアクションメソッドがそれを認識せず、IFormFileが常にnullであると報告することです。

編集:私は以前にコンテンツタイプとしてmultipart / form-dataを使用しようとしましたが、Kestrelの内臓で未処理の例外を与えました。これが正しい方法であり、jsonコンテンツタイプを使用することが私の元の問題の原因であることに気づきました。でもここからどこへ行けばいいのかわからない。いくつかのグーグルから、その例外が発生する原因は約10億ドルあることがわかります。

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情報ウェブホスト
POSTリクエストは48.1225ms500 text / htmlで終了しました。charset = utf-8
04:55:38.5333情報ケストレル
 接続ID "0HM4UHGE85O17"、リクエストID "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;
        }
      );
    });

Angular Service:

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 Controlle

// 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リクエストで送信されているデータは次のとおりです

POST http://192.168.0.16:1099/api/students/student-grades-report HTTP / 1.1
ホスト:192.168.0.16:1099
接続:キープアライブ
コンテンツの長さ: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
コンテンツタイプ:application / json
発信元:http:// localhost:3000
リファラー:http:// localhost:3000 /
Accept-Encoding:gzip、deflate
Accept-Language:en-US、en; q = 0.9、es-MX; q = 0.8、es; q = 0.7

------ WebKitFormBoundaryBVuZ7IbkjtQAKQ0a
Content-Disposition:form-data; name = "test1.PNG"; filename = "test1.PNG"
コンテンツタイプ:image / png

 PNG
 
  [画像ファイルのバイナリコンテンツ]  

------ WebKitFormBoundaryBVuZ7IbkjtQAKQ0a--

回答

2 jandrew Dec 12 2020 at 09:30

ファイルをフォームデータとして送信するため、正しいコンテンツタイプヘッダーを指定する必要があります。現在application/jsonContent-Typeヘッダーで送信しています。これは、APIを呼び出す場合にも当てはまりますが、最初は当然混乱する可能性があります。この場合の正しいコンテンツタイプはmultipart/form-dataです。IFormFileリクエストがJSONであると見なされるため、APIは認識していません。正しいcontent-typeヘッダー値で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リクエストにあるcontent-dispositionに注意することもできます。これは、添付されたファイルのタイプとともにフォームデータを示します。お役に立てれば。コードをテストするためにAngularプロジェクトを起動しませんでしたが、content-typeで問題が修正されるはずです。

編集:ファイルのフォームフィールドのキーとしてファイル名を使用していることに気付きました。フォームフィールドには「file」などのキーを使用する必要があります。これは、コントローラーコードのパラメーターの名前と一致する必要があります。コントローラコード内でファイルの実際のファイル名を取得できます。キーは、ファイルが添付されているフォームフィールドを示すだけです。例

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);

また、バックエンドApiを呼び出す関数でデータ検証を行うべきではないことにも注意してください。if(frmData)falseの場合、関数は何を返しますか?