如何将Angular上传的CSV文件发送至.NET Web服务并入库?
Great question! Let's break this down step by step—your core workflow idea is solid, but we can simplify it and fill in the missing pieces to get a working end-to-end solution.
First: Is Your Initial Workflow Correct?
Your high-level flow (Upload → Parse → Save to DB) is right, but you don't need a separate "Web Service" layer here. .NET Web API can handle both receiving the uploaded file and parsing it directly, then persisting to the database. Your existing .NET code is written to read a local file, which won't work for HTTP file uploads—that's a key gap we'll fix.
Key Gaps You're Missing
- Angular code to send the uploaded file to the API via HTTP (using
FormData) - A .NET Web API controller endpoint to accept the uploaded file stream
- Database persistence logic (using EF Core, for example)
- Error handling for file validation, CSV parsing failures, and database errors
- Proper handling of edge cases like malformed CSV rows (e.g., fields with commas inside quotes)
Full End-to-End Implementation
1. Angular Frontend
Updated HTML (fixed click binding and added validation)
<input type="file" accept=".csv" (change)="onFileSelected($event)" /> <button type="submit" (click)="onUpload()" class="button" [disabled]="!selectedFile">Upload</button>
TypeScript Component
import { Component } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Component({ selector: 'app-csv-upload', templateUrl: './csv-upload.component.html' }) export class CsvUploadComponent { selectedFile: File | null = null; constructor(private http: HttpClient) {} onFileSelected(event: Event): void { const input = event.target as HTMLInputElement; if (input.files?.length) { this.selectedFile = input.files[0]; // Quick file type check if (!this.selectedFile.name.endsWith('.csv')) { alert('Please select a valid CSV file!'); this.selectedFile = null; } } } onUpload(): void { if (!this.selectedFile) return; const formData = new FormData(); formData.append('csvFile', this.selectedFile); // Replace with your actual API URL this.http.post('https://your-api-domain/api/csv/upload', formData) .subscribe({ next: () => { alert('Upload successful! Data saved to database.'); this.selectedFile = null; }, error: (err) => { console.error('Upload failed:', err); alert('Oops, something went wrong. Check the console for details.'); } }); } }
2. .NET Web API Backend
First, install required NuGet packages:
Install-Package Microsoft.EntityFrameworkCore.SqlServer Install-Package CsvHelper # Optional but highly recommended for robust CSV parsing
Step 1: Define Entity and DbContext
// MonitoredEvent.cs public class MonitoredEvent { public int Id { get; set; } // Primary key public string Venue { get; set; } = string.Empty; public string Event { get; set; } = string.Empty; public string Section { get; set; } = string.Empty; public DateTime EventDate { get; set; } public TimeSpan Time { get; set; } public float TotalOrder { get; set; } public float TLC { get; set; } public string TicketType { get; set; } = string.Empty; public string Source { get; set; } = string.Empty; public string TicketPage { get; set; } = string.Empty; } // AppDbContext.cs public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } public DbSet<MonitoredEvent> MonitoredEvents { get; set; } }
Step 2: Create CSV Upload Controller
We'll cover two parsing options—your manual split approach, and a more robust one with CsvHelper.
Option 1: Manual Parsing (Based on Your Existing Code)
// CsvUploadController.cs [ApiController] [Route("api/csv")] public class CsvUploadController : ControllerBase { private readonly AppDbContext _dbContext; public CsvUploadController(AppDbContext dbContext) { _dbContext = dbContext; } [HttpPost("upload")] public async Task<IActionResult> UploadCsv(IFormFile csvFile) { // Validate input if (csvFile == null || csvFile.Length == 0) return BadRequest("No file was uploaded."); if (!Path.GetExtension(csvFile.FileName).Equals(".csv", StringComparison.OrdinalIgnoreCase)) return BadRequest("Please upload a CSV file."); var events = new List<MonitoredEvent>(); using (var reader = new StreamReader(csvFile.OpenReadStream())) { // Skip header row await reader.ReadLineAsync(); string line; while ((line = await reader.ReadLineAsync()) != null) { var values = line.Split(','); // Note: This will break if any fields have commas inside quotes! Use CsvHelper for that. if (values.Length < 10) continue; // Skip malformed rows var evt = new MonitoredEvent { Venue = values[0], Event = values[1], Section = values[2] }; // Parse date/time/numbers with error handling if (DateTime.TryParse(values[3], out var eventDate)) evt.EventDate = eventDate.Date; if (TimeSpan.TryParse(values[4], out var eventTime)) evt.Time = eventTime; if (float.TryParse(values[5], out var totalOrder)) evt.TotalOrder = totalOrder; if (float.TryParse(values[6], out var tlc)) evt.TLC = tlc; evt.TicketType = values[7]; evt.Source = values[8]; evt.TicketPage = values[9]; events.Add(evt); } } // Save to database await _dbContext.MonitoredEvents.AddRangeAsync(events); await _dbContext.SaveChangesAsync(); return Ok(new { TotalRecords = events.Count, Message = "Data saved successfully." }); } }
Option 2: Robust Parsing with CsvHelper
This handles edge cases like quoted fields with commas:
using CsvHelper; using System.Globalization; [HttpPost("upload")] public async Task<IActionResult> UploadCsv(IFormFile csvFile) { if (csvFile == null || csvFile.Length == 0) return BadRequest("No file was uploaded."); if (!Path.GetExtension(csvFile.FileName).Equals(".csv", StringComparison.OrdinalIgnoreCase)) return BadRequest("Please upload a CSV file."); List<MonitoredEvent> events; using (var reader = new StreamReader(csvFile.OpenReadStream())) using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) { // Map CSV headers to your entity properties (skip if headers match property names exactly) csv.Context.RegisterClassMap<MonitoredEventMap>(); csv.Read(); csv.ReadHeader(); events = csv.GetRecords<MonitoredEvent>().ToList(); } await _dbContext.MonitoredEvents.AddRangeAsync(events); await _dbContext.SaveChangesAsync(); return Ok(new { TotalRecords = events.Count, Message = "Data saved successfully." }); } // CSV mapping class (adjust if your CSV headers don't match property names) public class MonitoredEventMap : ClassMap<MonitoredEvent> { public MonitoredEventMap() { Map(m => m.Venue).Name("Venue"); Map(m => m.Event).Name("Event"); Map(m => m.Section).Name("Section"); Map(m => m.EventDate).Name("EventDate"); Map(m => m.Time).Name("Time"); Map(m => m.TotalOrder).Name("TotalOrder"); Map(m => m.TLC).Name("TLC"); Map(m => m.TicketType).Name("TicketType"); Map(m => m.Source).Name("Source"); Map(m => m.TicketPage).Name("TicketPage"); } }
Step 3: Configure Database Connection
Add this to appsettings.json:
"ConnectionStrings": { "DefaultConnection": "Server=YOUR_SERVER_NAME;Database=YOUR_DB_NAME;Trusted_Connection=True;TrustServerCertificate=True;" }
Register the DbContext in Program.cs:
builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
3. Pro Tips for Production
- Error Handling: Add global exception filters to catch and return friendly errors
- File Size Limits: Configure max upload size in
Program.cs:builder.Services.Configure<FormOptions>(options => { options.MultipartBodyLengthLimit = 10 * 1024 * 1024; // 10MB limit }); - Transactions: Wrap database operations in a transaction to ensure atomicity
- Logging: Add a logging library (like Serilog) to track uploads, parsing, and errors
内容的提问来源于stack exchange,提问作者BobbyWidThaTool

