InputStream, return 开始

InputStream, return to beginning

我有一个控制器post方法,需要上传文件:

查看:

@using (Html.BeginForm("UploadCSV", "TemporaryPerson", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">

        @Html.ValidationSummary(false, "", new { @class = "text-danger" })
        <br />
        <input type="file" name="personFile" /><input type="submit" value="Upload" />
    </div>
}

和控制器方法:

        [HttpPost]
        public ActionResult UploadCSV(UploadCsvViewModel model)
        {
            if (ModelState.IsValid)
            {
                using (StreamReader reader = new StreamReader(Request.Files[0].InputStream))
                {
                    using (var csv = new CsvReader(reader))
                    {
                        var records = csv.GetRecords<TemporaryPersonCsv>().ToList();
                        foreach (var record in records)
                        {
//...................
                        }
                    }
                }
            }
            return View();
       }

它工作正常。但我想将所有验证移至 UploadCsvViewModel:

public class UploadCsvViewModel : IValidatableObject
{
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var results = new List<ValidationResult>();

        if (HttpContext.Current.Request.Files == null || HttpContext.Current.Request.Files.Count == 0 || HttpContext.Current.Request.Files[0].ContentLength == 0)
            results.Add(new ValidationResult("File is not selected or empty", new string[] { "NoFile" }));

        using (StreamReader reader = new StreamReader(HttpContext.Current.Request.Files[0].InputStream))
        {
            using (var csv = new CsvReader(reader))
            {
                var enumeraterecords = csv.GetRecords<TemporaryPersonCsv>();

                if (enumeraterecords != null)
                {
                    var records = enumeraterecords.ToList();
                    if (records == null || records.Count == 0)
                        results.Add(new ValidationResult("No records in file", new string[] { "NoRecords" }));

                    // different validation, according with business logic

                }
                else
                    results.Add(new ValidationResult("No records in file", new string[] { "NoRecords" }));
            }
        }
        return results;
    }
}

问题是如果我在 UploadCsvViewModel 中有 Validate 方法,然后我得到错误:

Exception Details: CsvHelper.CsvReaderException: No header record was found.

在线:

var records = csv.GetRecords<TemporaryPersonCsv>().ToList();

在控制器方法中。我假设 InputStream 的位置无效。我试过了:

  1. 复制到另一个流:

    using (MemoryStream ms = new MemoryStream())
    {
        HttpContext.Current.Request.Files[0].InputStream.CopyTo(ms, HttpContext.Current.Request.Files[0].ContentLength);
    

没有帮助

  1. 在验证方法结束时将输入流位置设置为 0:

    HttpContext.Current.Request.Files[0].InputStream.Position = 0;

没有帮助

  1. 为 'reader' 对象调用 DiscardBufferedData

            reader.DiscardBufferedData();
    

也没有帮助。

我找到了解决方案。我将 InputStream 复制到另一个流(即 MemoryStream),然后我可以为两个流(InputStream 和 MemoryStream)设置 Position=0 并且它有效。

验证方法:

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var results = new List<ValidationResult>();

        if (HttpContext.Current.Request.Files == null || HttpContext.Current.Request.Files.Count == 0 || HttpContext.Current.Request.Files[0].ContentLength == 0)
            results.Add(new ValidationResult("File is not selected or empty", new string[] { "NoFile" }));

        using (MemoryStream ms = new MemoryStream())
        {
            HttpContext.Current.Request.Files[0].InputStream.CopyTo(ms);
            ms.Position = 0;
            using (StreamReader reader = new StreamReader(ms))
            {
                using (var csv = new CsvReader(reader))
                {
                    var enumeraterecords = csv.GetRecords<TemporaryPersonCsv>();

                    if (enumeraterecords != null)
                    {
                        var records = enumeraterecords.ToList();
                        if (records == null || records.Count == 0)
                            results.Add(new ValidationResult("No records in file", new string[] { "NoRecords" }));

                        // find dublicates in CSV file
                        var dublicates = (records.GroupBy(p => p.Fullname.ToLower().Trim()).Where(p => p.Count() > 1).Select(p => p.Key)).ToList();
                        if (dublicates != null && dublicates.Count > 0)
                        {
                            foreach (var dublicateFullname in dublicates)
                            {
                                results.Add(new ValidationResult(String.Concat("There are 2 or more records with fullname='", dublicateFullname, "' in your csv file. Please, fix your file and upload again."), new string[] { "DublicateRecords" }));
                            }
                        }

                        // find dublicates in TemporaryPersons and Persons tables
                        PlugandabandonEntities db = new PlugandabandonEntities();
                        foreach (var record in records)
                        {
                            if (db.TemporaryPerson.Where(p => p.Fullname.ToLower() == record.Fullname.ToLower()).Count() > 0)
                                results.Add(new ValidationResult(String.Concat("Record with fullname='", record.Fullname, "' already exists in TemporaryPersons. Please, fix your file and upload again."), new string[] { "RecordExistsInTemporaryPerson" }));

                            if (db.Person.Where(p => p.Fullname.ToLower() == record.Fullname.ToLower()).Count() > 0)
                                results.Add(new ValidationResult(String.Concat("Record with fullname='", record.Fullname, "' already exists in Persons. Please, fix your file and upload again."), new string[] { "RecordExistsInPersons" }));
                        }

                    }
                    else
                        results.Add(new ValidationResult("No records in file", new string[] { "NoRecords" }));
                }

                reader.DiscardBufferedData();
            }
        }

        HttpContext.Current.Request.Files[0].InputStream.Position = 0;

        return results;
    }

然后我们在控制器方法中有一个 "virgin" InputStream,我们可以根据需要使用它;)