为什么CsvHelper无法从MemoryStream中读取?

19

我正在尝试将一个上传的CSV文件转换为对象,以便可以保存在数据库中。在控制器中,我使用CsvHelper。

但看起来这只有在我先保存文件并从中读取时才能正常工作。CsvHelper不能直接从内存流中处理文件内容。在下面的代码中,第一个GetRecords返回空。

    [HttpPost]
    [Route(ApiRoutes.EodVariationMarginPlugs)]
    public async Task<IActionResult> UploadPlugAsync(IFormFile filePayload)
    {

        if (filePayload.Length > 0)
        {

            using (var stream = new MemoryStream())
            {
                filePayload.CopyTo(stream);
                using (var reader = new StreamReader(stream))
                using (var csv = new CsvReader(reader))
                {
                    csv.Configuration.RegisterClassMap<EodVariationMarginPlugMap>();
                    csv.Configuration.MissingFieldFound = null;
                    var records = csv.GetRecords<EodVariationMarginPlug>().ToList(); // record count is 0
                    foreach (var p in records)
                    {
                        p.CreatedAt = DateTimeOffset.Now;
                        p.CreatedBy = HttpContext.User.Identity.Name;
                    }
                    await _repository.InsertPlugsAsync(records);
                }
            }

        var fileName = ContentDispositionHeaderValue
            .Parse(filePayload.ContentDisposition)
            .FileName.ToString().Trim('"');
            var path = Path.Combine(Path.GetTempPath(), fileName);
            using (var fileStream = new FileStream(path, FileMode.Create))
            {
                await filePayload.CopyToAsync(fileStream);
            }
            var textReader = System.IO.File.OpenText(path);
            using (var csv = new CsvReader(textReader))
            {
                csv.Configuration.RegisterClassMap<EodVariationMarginPlugMap>();
                csv.Configuration.MissingFieldFound = null;
                var records = csv.GetRecords<EodVariationMarginPlug>().ToList();
                foreach (var p in records)
                {
                    p.CreatedAt = DateTimeOffset.Now;
                    p.CreatedBy = HttpContext.User.Identity.Name;
                }
                await _repository.InsertPlugsAsync(records);
            }
        }
        return Ok();
    }

你可能需要清空流并将其返回到开头。 - user47589
谢谢@Amy,我也认为应该是这样,所以在filePayload.CopyTo(stream);之后添加了stream.Position = 0;,但是CsvHelperMemoryStream中没有得到任何东西。 - Anand
1个回答

26

在这里最常见的错误是忘记 MemoryStream 是二进制的;它处理字节。你需要处理字符的东西,并不总是一对一的适配。好消息是你通过将 MemoryStream 包装在 StreamReader 中避免了这个错误:

using (var reader = new StreamReader(stream))

StreamReader 实现了 TextReader,它处理的是字符而不是字节。太好了!不过坏消息是 `StreamReader` 是在这一行代码之后创建的:

filePayload.CopyTo(stream);

问题在于该行代码将流指针指向了数据的末尾。当您试图从其中读取时,流中已经没有内容了。

要修复这个问题,您只需要将指针回溯到开头。因此,应该执行以下操作:

using (var stream = new MemoryStream())
{
    filePayload.CopyTo(stream);
    using (var reader = new StreamReader(stream))
using (var stream = new MemoryStream())
{
    filePayload.CopyTo(stream);
    stream.Seek(0, SeekOrigin.Begin);

    using (var reader = new StreamReader(stream))

Joel Coehoorn - 非常感谢! - Anand
1
2019年对我也起作用,只需在将memoryStream传递给new csvReader(ms)之前加上stream.Seek(0, SeekOrigin.Begin);这一行即可。谢谢Joel。 - James Gould

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接