【发布时间】:2023-03-22 08:59:01
【问题描述】:
我有一个上传 CSV 文件的有效解决方案。目前,我使用IFormCollection 让用户从一个视图上传多个 CSV 文件。
CSV 文件保存为临时文件,如下所示:
List<string> fileLocations = new List<string>();
foreach (var formFile in files)
{
filePath = Path.GetTempFileName();
if (formFile.Length > 0)
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await formFile.CopyToAsync(stream);
}
}
fileLocations.Add(filePath);
}
我将文件位置列表发送到另一种方法(就在下面)。我遍历文件位置并从临时文件中流式传输数据,然后使用数据表和SqlBulkCopy 插入数据。我目前一次上传 50 到 200 个文件,每个文件大约 330KB。插入一百个,大约需要 6 分钟,也就是 30-35MB 左右。
public void SplitCsvData(string fileLocation, Guid uid)
{
MetaDataModel MetaDatas;
List<RawDataModel> RawDatas;
var reader = new StreamReader(File.OpenRead(fileLocation));
List<string> listRows = new List<string>();
while (!reader.EndOfStream)
{
listRows.Add(reader.ReadLine());
}
var metaData = new List<string>();
var rawData = new List<string>();
foreach (var row in listRows)
{
var rowName = row.Split(',')[0];
bool parsed = int.TryParse(rowName, out int result);
if (parsed == false)
{
metaData.Add(row);
}
else
{
rawData.Add(row);
}
}
//Assigns the vertical header name and value to the object by splitting string
RawDatas = GetRawData.SplitRawData(rawData);
SaveRawData(RawDatas);
MetaDatas = GetMetaData.SplitRawData(rawData);
SaveRawData(RawDatas);
}
然后此代码将对象传递给 以创建数据表并插入数据。
private DataTable CreateRawDataTable
{
get
{
var dt = new DataTable();
dt.Columns.Add("Id", typeof(int));
dt.Columns.Add("SerialNumber", typeof(string));
dt.Columns.Add("ReadingNumber", typeof(int));
dt.Columns.Add("ReadingDate", typeof(string));
dt.Columns.Add("ReadingTime", typeof(string));
dt.Columns.Add("RunTime", typeof(string));
dt.Columns.Add("Temperature", typeof(double));
dt.Columns.Add("ProjectGuid", typeof(Guid));
dt.Columns.Add("CombineDateTime", typeof(string));
return dt;
}
}
public void SaveRawData(List<RawDataModel> data)
{
DataTable dt = CreateRawDataTable;
var count = data.Count;
for (var i = 1; i < count; i++)
{
DataRow row = dt.NewRow();
row["Id"] = data[i].Id;
row["ProjectGuid"] = data[i].ProjectGuid;
row["SerialNumber"] = data[i].SerialNumber;
row["ReadingNumber"] = data[i].ReadingNumber;
row["ReadingDate"] = data[i].ReadingDate;
row["ReadingTime"] = data[i].ReadingTime;
row["CombineDateTime"] = data[i].CombineDateTime;
row["RunTime"] = data[i].RunTime;
row["Temperature"] = data[i].Temperature;
dt.Rows.Add(row);
}
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlTransaction tr = conn.BeginTransaction())
{
using (var sqlBulk = new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, tr))
{
sqlBulk.BatchSize = 1000;
sqlBulk.DestinationTableName = "RawData";
sqlBulk.WriteToServer(dt);
}
tr.Commit();
}
}
}
是否有其他方法或更好的方法来提高性能,从而减少上传时间,因为上传时间可能很长,而且我发现内存使用量不断增加至 500MB 左右。
TIA
【问题讨论】:
-
通过摆脱数据表。现在,您正在将整个表加载到内存中,然后对其进行解析并在内存中制作 another 副本,并且仅在最后将表写入数据库。
WriteToServer也可以接受 DbDataReader。如果您找到一种在文件顶部创建数据读取器的方法,您将能够将文件中的记录直接泵送到 SqlBulkCopy -
BTW CsvHelper 可以产生a data reader from any stream reader directly
-
不要填写
List<string>,而是直接填写DataTable。如果您在SaveRawData中有一个BatchSize,则将其设为一个字段并在while循环中检查DataTable.Rows.Count==MaxBatchSize。然后你可以把它传递给SaveRawData。通过它后,创建一个新的空表。这样,您的内存中就只有您当前正在处理的内容
标签: c# csv datatable asp.net-core-2.0 sqlbulkcopy