Tải tệp từ Angular lên ASP.NET Core
Lần đầu tiên tôi đang cố gắng tải tệp từ thành phần Angular lên trang web ASPNET Core và chỉ đơn giản là không thể làm cho nó hoạt động. Hy vọng rằng những đoạn mã sau đây sẽ đủ để cho thấy những điều cần thiết của những gì đang diễn ra. Vấn đề là mặc dù tôi xác nhận rằng tham số được truyền cho phương thức đăng của HttpClient (frmData) là hợp lệ, nhưng phương thức hành động ASPNet Core không bao giờ nhìn thấy nó và báo cáo rằng IFormFile luôn rỗng.
CHỈNH SỬA: Trước đây tôi đã thử sử dụng multiart / form-data làm kiểu nội dung nhưng tôi đã đưa ra một ngoại lệ chưa được khắc phục trong ruột của Kestrel. Bây giờ tôi nhận ra rằng đây là cách chính xác để làm điều đó và việc sử dụng kiểu nội dung json là nguồn gốc của vấn đề GỐC của tôi. Nhưng tôi không biết phải đi đâu từ đây. Tôi thấy từ một số googling, có khoảng \ tỷ nguyên nhân khác nhau khiến ngoại lệ đó xảy ra.
POST Thực thi điểm cuối 'JovenesA.Controllers.StudentssController.PostStudentGradesReport (JAWebAPI)'
04: 55: 38.4853 Info ControllerActionInvoker
POST Tuyến đối sánh với {action = "PostStudentGradesReport", controller = "Becas"}. Thực thi hành động JovenesA.Controllers.BecasController.PostStudentGradesReport (JAWebAPI)
04: 55: 38.5032 Lỗi DeveloperExceptionPageMiddleware
BÀI ĐĂNG Một ngoại lệ chưa được xử lý đã xảy ra khi thực hiện yêu cầu.
04: 55: 38.5333 Thông tin WebHost
Yêu cầu POST hoàn tất trong 48,1225ms 500 text / html; charset = utf-8
04: 55: 38.5333 Thông tin Kestrel
Id kết nối "0HM4UHGE85O17", id yêu cầu "0HM4UHGE85O17: 00000006": ứng dụng đã hoàn thành mà không cần đọc toàn bộ nội dung yêu cầu.
Mọi sự trợ giúp sẽ rất được trân trọng!
Thành phần Angular:
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;
}
);
});
Dịch vụ Angular:
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];
Nếu hữu ích, đây là dữ liệu đang được gửi trong yêu cầu ĐĂNG
ĐĂNG http://192.168.0.16:1099/api/students/student-grades-report HTTP / 1.1 Máy chủ: 192.168.0.16:1099 Kết nối: tiếp tục tồn tại Nội dung-Độ dài: 13561 Chấp nhận: ứng dụng / json, văn bản / đơn giản, * / * ĐNT: 1 Tác nhân người dùng: Mozilla / 5.0 (Windows NT 10.0; Win64; x64) AppleWebKit / 537.36 (KHTML, như Gecko) Chrome / 87.0.4280.88 Safari / 537.36 Loại nội dung: ứng dụng / json Nguồn gốc: http: // localhost: 3000 Người giới thiệu: http: // localhost: 3000 / Chấp nhận mã hóa: gzip, deflate Ngôn ngữ chấp nhận: en-US, en; q = 0,9, es-MX; q = 0,8, es; q = 0,7 ------ WebKitFormBoundaryBVuZ7IbkjtQAKQ0a Nội dung-Bố trí: biểu mẫu-dữ liệu; tên = "test1.PNG"; filename = "test1.PNG" Loại nội dung: image / png PNG [nội dung nhị phân của tệp hình ảnh] ------ WebKitFormBoundaryBVuZ7IbkjtQAKQ0a--
Trả lời
Bạn đang gửi tệp dưới dạng dữ liệu biểu mẫu, vì vậy bạn cần chỉ định tiêu đề loại nội dung chính xác. Hiện bạn đang gửi application/jsontrong Content-Typetiêu đề. Điều này đúng ngay cả khi gọi một API, điều này có thể gây nhầm lẫn lúc đầu. Loại nội dung chính xác trong trường hợp này là multipart/form-data. API của bạn không nhìn thấy IFormFilevì nó cho rằng yêu cầu là JSON. Tôi đã sửa đổi mã Angular của bạn với giá trị tiêu đề loại nội dung chính xác.
Chỉnh sửa: Hóa ra rằng việc chỉ định Content-Typetiêu đề theo cách thủ công sẽ khiến các giá trị ranh giới không được đặt tự động trong giá trị tiêu đề. Thay vào đó, giải pháp đơn giản là không tự thêm tiêu đề, điều này sẽ dẫn đến các giá trị ranh giới và kiểu nội dung thích hợp được đặt tự động. Nếu bạn tự đặt tiêu đề, bạn cũng sẽ phải đặt các giá trị ranh giới. Đối với hầu hết các tình huống, để nó mặc định có lẽ là giải pháp tốt nhất. Liên kết đến câu hỏi / câu trả lời chỉ ra điều này. FormData cách lấy hoặc đặt ranh giới trong multiart / 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 });
}
}
Bạn cũng có thể lưu ý bố cục nội dung trong yêu cầu HTTP mà bạn đã cung cấp, nó hiển thị dữ liệu biểu mẫu cùng với loại tệp được đính kèm. Hi vọng điêu nay co ich. Tôi đã không kích hoạt một dự án Angular để kiểm tra mã của bạn nhưng loại nội dung sẽ khắc phục sự cố của bạn.
Chỉnh sửa : Tôi nhận thấy rằng bạn đang sử dụng tên tệp làm khóa cho trường biểu mẫu có tệp. Bạn cần sử dụng một khóa chẳng hạn như chỉ 'tệp' cho trường biểu mẫu, khóa này phải khớp với tên của tham số trong mã bộ điều khiển của bạn. Bạn có thể lấy tên tệp thực của tệp trong mã bộ điều khiển của mình, khóa chỉ đơn giản cho biết (các) tệp được đính kèm vào trường biểu mẫu nào. Thí dụ
frmData.append('file', file);
Và sau đó cho hành động điều khiển của bạn
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();
}
Tôi không thể đảm bảo rằng điều này sẽ hoạt động, nhưng bạn có thể thử sử dụng HttpRequest của Angular. Vì vậy, trong dịch vụ góc cạnh của bạn, hãy thử điều này:
const request = new HttpRequest (
'POST',
url, // http://localhost/your_endpoint
frmData,
{ withCredentials: false }
);
return this.http.request(request);
Cũng lưu ý rằng bạn không nên xác thực dữ liệu trong hàm gọi phụ trợ Api. Hàm của bạn sẽ trả về if(frmData)là gì nếu sai?