อัปโหลดไฟล์จาก Angular ไปยัง ASP.NET Core

Dec 11 2020

เป็นครั้งแรกที่ฉันพยายามอัปโหลดไฟล์จากส่วนประกอบเชิงมุมไปยังหน้าเว็บ ASPNET Core และไม่สามารถใช้งานได้ หวังว่าข้อความที่ตัดตอนมาของโค้ดต่อไปนี้จะเพียงพอที่จะแสดงข้อมูลสำคัญของสิ่งที่เกิดขึ้น ปัญหาคือแม้ว่าฉันจะยืนยันว่าพารามิเตอร์ที่ส่งไปยังเมธอดการโพสต์ของ HttpClient (frmData) นั้นถูกต้อง แต่วิธีการดำเนินการของ ASPNet Core จะไม่เห็นมันและรายงานว่า IFormFile เป็นโมฆะเสมอ

แก้ไข: ก่อนหน้านี้ฉันเคยลองใช้หลายส่วน / แบบฟอร์มข้อมูลเป็นประเภทเนื้อหา แต่ฉันให้ข้อยกเว้นที่ไม่สามารถจัดการได้ในความกล้าของ Kestrel ฉันรู้แล้วว่านี่เป็นวิธีที่ถูกต้องในการทำและการใช้ประเภทเนื้อหา json เป็นที่มาของปัญหาดั้งเดิมของฉัน แต่ฉันไม่รู้ว่าจะไปจากที่นี่ ฉันเห็นจาก googling บางอย่างมีสาเหตุที่แตกต่างกันประมาณพันล้านสาเหตุที่ทำให้เกิดข้อยกเว้น

POST ดำเนินการจุดสิ้นสุด 'JovenesA.Controllers.StudentssController.PostStudentGradesReport (JAWebAPI)'
04: 55: 38.4853 Info ControllerActionInvoker
POST เส้นทางตรงกับ {action = "PostStudentGradesReport", controller = "Becas"} การดำเนินการดำเนินการ JovenesA.Controllers.BecasController.PostStudentGradesReport (JAWebAPI)
04: 55: 38.5032 ข้อผิดพลาด DeveloperExceptionPageMiddleware
POST มีข้อยกเว้นที่ไม่สามารถจัดการได้เกิดขึ้นขณะดำเนินการตามคำขอ
04: 55: 38.5333 ข้อมูล WebHost
คำขอ POST เสร็จสิ้นใน 48.1225ms 500 text / html; charset = utf-8
04: 55: 38.5333 ข้อมูล Kestrel
 รหัสการเชื่อมต่อ "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 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

โพสต์ 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
User-Agent: 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 /
ยอมรับการเข้ารหัส: gzip, deflate
ภาษาที่ยอมรับ: en-US, en; q = 0.9, es-MX; q = 0.8, es; q = 0.7

------ WebKitFormBoundaryBVuZ7IbkjtQAKQ0a
Content-Disposition: form-data; ชื่อ = "test1.PNG"; ชื่อไฟล์ = "test1.PNG"
ประเภทเนื้อหา: image / png

 PNG
 
  [เนื้อหาไบนารีของไฟล์รูปภาพ]  

------ WebKitFormBoundaryBVuZ7IbkjtQAKQ0a--

คำตอบ

2 jandrew Dec 12 2020 at 09:30

คุณกำลังส่งไฟล์เป็นข้อมูลแบบฟอร์มดังนั้นคุณต้องระบุส่วนหัวประเภทเนื้อหาที่ถูกต้อง ขณะนี้การส่งของคุณapplication/jsonในContent-Typeส่วนหัว แม้ว่าจะเรียก API ซึ่งอาจทำให้เกิดความสับสนได้ในตอนแรก multipart/form-dataประเภทเนื้อหาที่ถูกต้องในกรณีนี้คือ API ของคุณไม่เห็นIFormFileเนื่องจากคิดว่าคำขอเป็น JSON ฉันได้แก้ไขโค้ดเชิงมุมของคุณด้วยค่าส่วนหัวประเภทเนื้อหาที่ถูกต้อง

แก้ไข:ปรากฎว่าการระบุContent-Typeส่วนหัวด้วยตนเองจะทำให้ค่าขอบเขตไม่ถูกตั้งค่าโดยอัตโนมัติในค่าส่วนหัว วิธีแก้ปัญหาง่ายๆคืออย่าเพิ่มส่วนหัวด้วยตัวเองซึ่งจะส่งผลให้ค่าประเภทเนื้อหาและขอบเขตที่เหมาะสมถูกกำหนดโดยอัตโนมัติ หากคุณตั้งค่าส่วนหัวด้วยตัวคุณเองคุณจะต้องกำหนดค่าขอบเขตด้วยเช่นกัน สำหรับสถานการณ์ส่วนใหญ่การปล่อยให้เป็นค่าเริ่มต้นน่าจะเป็นทางออกที่ดีที่สุด เชื่อมโยงไปยังคำถาม / คำตอบที่ชี้ให้เห็น FormData วิธีรับหรือกำหนดขอบเขตในหลายส่วน / แบบฟอร์มข้อมูล - เชิงมุม

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 เพื่อทดสอบโค้ดของคุณ แต่ประเภทเนื้อหาควรแก้ไขปัญหาของคุณได้

แก้ไข : ฉันสังเกตเห็นว่าคุณใช้ชื่อไฟล์เป็นคีย์สำหรับฟิลด์แบบฟอร์มกับไฟล์ คุณต้องใช้คีย์เช่น '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

ฉันไม่สามารถรับประกันได้ว่าจะได้ผล แต่คุณสามารถลองใช้ HttpRequest ของ Angular ดังนั้นในบริการเชิงมุมของคุณให้ลองทำดังนี้:

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

โปรดทราบว่าคุณไม่ควรทำการตรวจสอบข้อมูลในฟังก์ชันที่เรียกใช้แบ็กเอนด์ Api ฟังก์ชันของคุณจะส่งคืนอะไรหากif(frmData)เป็นเท็จ