Web API接收上传CSV字节数组,结合EF插入含附加字段的SQL Server数据
解决方案分步说明
1. 处理字节数组,解析CSV内容
首先得把内存里的字节数组转成可读取的流,然后用专业的CSV解析库处理——自己写解析逻辑很容易踩坑(比如带引号的字段、跨行内容这些情况),推荐用CsvHelper(直接通过NuGet安装就行)。
在Web API控制器里,先从表单数据里拿到三个附加字段,再处理CSV字节数组:
using CsvHelper; using System.Globalization; using System.IO; using System.Data.SqlClient; [HttpPost("upload-csv")] public async Task<IActionResult> UploadCsv() { // 读取上传的表单数据 var form = await Request.ReadFormAsync(); var file = form.Files["csvFile"]; var field1 = form["field1"].ToString(); // 第一个附加字段 var field2 = form["field2"].ToString(); // 第二个附加字段 var field3 = form["field3"].ToString(); // 第三个附加字段 if (file == null || file.Length == 0) return BadRequest("未上传有效文件"); // 将文件流转为内存流(如果已经是字节数组,直接用MemoryStream包装即可) using var stream = new MemoryStream(); await file.CopyToAsync(stream); stream.Position = 0; // 重置流的读取位置到开头 // 用CsvHelper读取CSV内容 using var reader = new StreamReader(stream); using var csv = new CsvReader(reader, CultureInfo.InvariantCulture); // 定义CSV对应的实体类,字段和CSV表头对应 var csvRecords = csv.GetRecords<CsvRecord>().ToList(); // 给每条CSV记录添加上传的附加字段,映射到SQL表实体 var recordsToInsert = csvRecords.Select(record => new YourSqlTableEntity { // 映射CSV字段到SQL表字段 CsvColumn1 = record.Field1, CsvColumn2 = record.Field2, // ... 其他CSV字段 // 添加上传的附加字段 ExtraField1 = field1, ExtraField2 = field2, ExtraField3 = field3 }).ToList(); // 批量插入到SQL Server await BulkInsertToSql(recordsToInsert); return Ok($"成功插入 {recordsToInsert.Count} 条记录"); }
2. 定义对应的实体类
需要两个实体类:一个对应CSV的结构,一个对应SQL Server表的结构:
// CSV记录实体,字段名要和CSV表头匹配,也可以用特性配置映射 public class CsvRecord { public string Field1 { get; set; } public string Field2 { get; set; } // ... 其他CSV列对应的属性 } // SQL Server表对应的实体,字段名要和表列名匹配 public class YourSqlTableEntity { public string CsvColumn1 { get; set; } public string CsvColumn2 { get; set; } public string ExtraField1 { get; set; } public string ExtraField2 { get; set; } public string ExtraField3 { get; set; } // ... 其他表列属性 }
3. 实现高效批量插入
用SqlBulkCopy做批量插入,比逐条执行Insert语句效率高太多,尤其适合大量数据的场景:
private async Task BulkInsertToSql(List<YourSqlTableEntity> records) { var connectionString = "你的SQL Server连接字符串"; // 替换成实际连接串 using var connection = new SqlConnection(connectionString); await connection.OpenAsync(); using var bulkCopy = new SqlBulkCopy(connection); bulkCopy.DestinationTableName = "你的SQL表名"; // 替换成目标表名 // 映射实体属性到表列(如果属性名和列名完全一致,可以省略这一步) bulkCopy.ColumnMappings.Add("CsvColumn1", "CsvColumn1"); bulkCopy.ColumnMappings.Add("CsvColumn2", "CsvColumn2"); bulkCopy.ColumnMappings.Add("ExtraField1", "ExtraField1"); bulkCopy.ColumnMappings.Add("ExtraField2", "ExtraField2"); bulkCopy.ColumnMappings.Add("ExtraField3", "ExtraField3"); // ... 其他列的映射 // 把实体列表转成DataTable(也可以用IEnumerable直接传入,DataTable兼容性更强) var dataTable = new DataTable(); dataTable.Columns.Add("CsvColumn1", typeof(string)); dataTable.Columns.Add("CsvColumn2", typeof(string)); dataTable.Columns.Add("ExtraField1", typeof(string)); dataTable.Columns.Add("ExtraField2", typeof(string)); dataTable.Columns.Add("ExtraField3", typeof(string)); foreach (var record in records) { dataTable.Rows.Add(record.CsvColumn1, record.CsvColumn2, record.ExtraField1, record.ExtraField2, record.ExtraField3); } await bulkCopy.WriteToServerAsync(dataTable); }
4. 关键注意事项
- CSV表头匹配:如果CSV表头和
CsvRecord属性名不一致,可以用[Name("表头名称")]特性配置映射,比如:public class CsvRecord { [Name("用户ID")] public string UserId { get; set; } } - 数据类型转换:如果CSV里有数字、日期等类型,直接在
CsvRecord里用对应类型即可,CsvHelper会自动处理格式转换。 - 大文件处理:如果CSV文件很大,别一次性把所有记录读到内存,改用
csv.Read()逐行读取,每积累一定数量(比如1000条)就批量插入一次,避免内存溢出:var batchSize = 1000; var batch = new List<YourSqlTableEntity>(); while (csv.Read()) { var record = csv.GetRecord<CsvRecord>(); batch.Add(new YourSqlTableEntity { /* 映射字段 */ }); if (batch.Count >= batchSize) { await BulkInsertToSql(batch); batch.Clear(); } } // 插入剩余的记录 if (batch.Count > 0) await BulkInsertToSql(batch); - 异常处理:记得加try-catch块,捕获解析错误、数据库连接错误等情况,返回友好的错误信息。
AngularJS端的小提醒
确保上传时把附加字段和文件一起放到FormData里:
var formData = new FormData(); formData.append('csvFile', file); formData.append('field1', $scope.formField1); formData.append('field2', $scope.formField2); formData.append('field3', $scope.formField3); Upload.upload({ url: '/api/your-route/upload-csv', data: formData }).then(function (resp) { console.log('上传成功:' + resp.data); }, function (resp) { console.log('上传失败,状态码:' + resp.status); });
内容的提问来源于stack exchange,提问作者B-Ray
相关产品推荐
相关产品推荐

