Reputation: 129
I need to write a HttpPostedFileBase
csv (and save it) after mapping it into a list using the CsvHelper nuget package. However, after mapping it with CsvHelper, the ContentLength
is 0, and I end up saving an empty .csv file.
CsvHelper itself states that the Read method should not be used when using the GetRecords<T>()
method.
// Summary:
// Gets all the records in the CSV file and converts each to System.Type T. The
// Read method should not be used when using this.
//
// Type parameters:
// T:
// The System.Type of the record.
//
// Returns:
// An System.Collections.Generic.IEnumerable`1 of records.
public virtual IEnumerable<T> GetRecords<T>();
I tried placing it into a copy variable:
HttpPostedFileBase csvCopy = csvFile;
But this didn't work. Tried some other solutions I found on stackoverflow, which didn't work either. I "solved" this problem by sending the same file twice to the controller as a parameter. Then I use the first one with CsvHelper, and I read and save the other one.
public async Task<ActionResult> ImportCSV(HttpPostedFileBase csvFile, HttpPostedFileBase csvFileCopy)
However, I think this is a bad solution. I would like to use a single file, map it, reread it and save it.
Mapping it into a list:
using(var reader = new StreamReader(csvFile.InputStream)) {
using(var csvReader = new CsvReader(reader)) {
csvReader.Configuration.RegisterClassMap(new CSVModelMap(mapDictionary));
csvReader.Configuration.BadDataFound = null;
csvReader.Configuration.HeaderValidated = null;
csvReader.Configuration.MissingFieldFound = null;
importData = csvReader.GetRecords<CSVModel>().ToList();
}
}
Saving it:
var fileName = serverPath + "\\" + hashedFileName;
CheckIfDirectoryExists(serverPath);
var reader = new StreamReader(csvFile.InputStream);
var csvContent = await reader.ReadToEndAsync();
File.WriteAllText(fileName, csvContent);
Upvotes: 2
Views: 2128
Reputation: 821
This is an old post but wanted to provide an answer from CSVHelper using your example as reference for saving the importData
back into a .csv file.
https://joshclose.github.io/CsvHelper/examples/writing/write-class-objects/
// ...
importData = csvReader.GetRecords<CSVModel>().ToList();
using (var writer = new StreamWriter("path\\to\\file.csv"))
{
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(importData);
}
writer.Close();
}
Upvotes: 0
Reputation: 129
The issue was CsvHelper:
using(var csvReader = new CsvReader(reader))
At the end of the using statement, CsvReader
closes the reader. Then when I try
csvFile.InputStream.Seek(0, SeekOrigin.Begin);
or
reader.BaseStream.Position = 0;
it throws a NullReferenceException
. I solved this by simply overriding the CsvReader
:
using (var csvReader = new CsvReader(reader, true))
true
being leaveOpen
:
// Summary:
// Creates a new CSV reader using the given System.IO.TextReader.
//
// Parameters:
// reader:
// The reader.
//
// leaveOpen:
// true to leave the reader open after the CsvReader object is disposed, otherwise
// false.
public CsvReader(TextReader reader, bool leaveOpen);
Then I set the position back to 0, using reader.BaseStream.Position = 0;
, and then after saving the file, dispose the reader.
Upvotes: 1
Reputation: 486
I'm not to sure how GetRecords works, but there might be the possibility that it leaves the cursor on the stream pointed towards the end.
Meaning on your saving sequence you start reading the InputStream at the end, which results in no data to read.
So you could try
csvFile.InputStream.Seek(0, SeekOrigin.Begin)
EDIT:
For your try to copy the stream you only copy the reference to the object and not the object itself.
To copy the stream data you would need to use the method CopyTo(stream)
which would leave the cursor on the end of the stream, so here seek is needed for sure.
Upvotes: 1