Unggah file dari Angular ke ASP.NET Core

Dec 11 2020

Untuk pertama kalinya saya mencoba mengunggah file dari komponen Angular ke halaman web ASPNET Core, dan tidak bisa membuatnya berfungsi. Semoga kutipan kode berikut cukup untuk menunjukkan hal-hal penting dari apa yang sedang terjadi. Masalahnya adalah meskipun saya mengonfirmasi bahwa parameter yang diteruskan ke metode posting HttpClient (frmData) valid, metode tindakan ASPNet Core tidak pernah melihatnya, dan melaporkan bahwa IFormFile selalu null.

EDIT: Saya sebelumnya mencoba menggunakan multipart / form-data sebagai jenis konten tetapi saya memberikan pengecualian yang tidak tertangani di nyali Kestrel. Saya sekarang menyadari bahwa ini adalah cara yang benar untuk melakukannya, dan menggunakan tipe konten json adalah sumber masalah ASLI saya. Tapi saya tidak tahu harus pergi ke mana setelah ini. Saya melihat dari beberapa googling ada sekitar \ miliar penyebab berbeda untuk pengecualian itu terjadi.

POST Executing endpoint 'JovenesA.Controllers.StudentssController.PostStudentGradesReport (JAWebAPI)'
04: 55: 38.4853 Info ControllerActionInvoker
Rute POST cocok dengan {action = "PostStudentGradesReport", controller = "Becas"}. Menjalankan tindakan JovenesA.Controllers.BecasController.PostStudentGradesReport (JAWebAPI)
04: 55: 38.5032 Error DeveloperExceptionPageMiddleware
POST Pengecualian tidak tertangani terjadi saat menjalankan permintaan.
04: 55: 38.5333 Info WebHost
Permintaan POST selesai dalam teks / html 48,1225ms 500; charset = utf-8
04: 55: 38.5333 Info Kestrel
 ID koneksi "0HM4UHGE85O17", ID permintaan "0HM4UHGE85O17: 00000006": aplikasi diselesaikan tanpa membaca seluruh isi permintaan.

Bantuan apa pun akan sangat dihargai!

Komponen Sudut:

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

Layanan Sudut:

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

Kontrol Inti ASPNET

// 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];

Jika membantu, berikut adalah data yang dikirim dalam permintaan POST

POST http://192.168.0.16:1099/api/students/student-grades-report HTTP / 1.1
Tuan rumah: 192.168.0.16:1099
Koneksi: tetap hidup
Panjang Konten: 13561
Terima: application / json, text / plain, * / *
DNT: 1
Agen Pengguna: Mozilla / 5.0 (Windows NT 10.0; Win64; x64) AppleWebKit / 537.36 (KHTML, seperti Gecko) Chrome / 87.0.4280.88 Safari / 537.36
Jenis Konten: application / json
Asal: http: // localhost: 3000
Perujuk: http: // localhost: 3000 /
Terima-Enkode: gzip, deflate
Terima-Bahasa: en-US, en; q = 0.9, es-MX; q = 0.8, es; q = 0.7

------ WebKitFormBoundaryBVuZ7IbkjtQAKQ0a
Isi-Disposisi: form-data; name = "test1.PNG"; nama file = "test1.PNG"
Jenis-Konten: image / png

 PNG
 
  [konten biner dari file gambar]  

------ WebKitFormBoundaryBVuZ7IbkjtQAKQ0a--

Jawaban

2 jandrew Dec 12 2020 at 09:30

Anda mengirim file sebagai data formulir, jadi Anda perlu menentukan header tipe konten yang benar. Saat ini Anda mengirimkan application/jsondi Content-Typeheader. Ini benar bahkan saat memanggil API, yang awalnya bisa membingungkan. Jenis konten yang benar dalam kasus ini adalah multipart/form-data. API Anda tidak dapat melihat IFormFilekarena mengira permintaannya adalah JSON. Saya telah memodifikasi kode Angular Anda dengan nilai header tipe konten yang benar.

Edit: Ternyata menentukan Content-Typeheader secara manual akan menyebabkan nilai batas tidak disetel secara otomatis di nilai header. Sebagai gantinya, solusi sederhananya adalah dengan tidak menambahkan tajuk sendiri, yang akan menghasilkan tipe konten yang tepat dan nilai batas untuk ditetapkan secara otomatis. Jika Anda menyetel tajuk sendiri, Anda juga harus menyetel nilai batasnya juga. Untuk sebagian besar situasi, membiarkannya default mungkin merupakan solusi terbaik. Tautan ke pertanyaan / jawaban yang menunjukkan hal ini. FormData cara mendapatkan atau mengatur batas dalam 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 });
    }
}

Anda juga dapat mencatat disposisi konten yang ada pada permintaan HTTP yang Anda berikan, yang menunjukkan data formulir bersama dengan jenis file yang dilampirkan. Semoga ini membantu. Saya tidak menjalankan proyek Angular untuk menguji kode Anda tetapi tipe konten harus memperbaiki masalah Anda.

Edit : Saya perhatikan bahwa Anda menggunakan nama file sebagai kunci untuk bidang formulir dengan file tersebut. Anda perlu menggunakan kunci seperti hanya 'file' untuk bidang formulir, yang harus cocok dengan nama parameter dalam kode pengontrol Anda. Anda bisa mendapatkan nama file sebenarnya dari file dalam kode pengontrol Anda, kuncinya hanya menunjukkan bidang formulir mana file tersebut dilampirkan. Contoh

frmData.append('file', file);

Dan kemudian untuk tindakan pengontrol Anda

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

Saya tidak dapat menjamin bahwa ini akan berhasil, tetapi Anda dapat mencoba menggunakan HttpRequest Angular. Jadi dalam layanan sudut Anda, coba ini:

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

Perhatikan juga bahwa Anda tidak boleh melakukan validasi data dalam fungsi yang memanggil Api backend. Apa fungsi Anda akan kembali jika if(frmData)salah?