Say you have this Action:
public List<string> Index(IFormFile file){ //extract list of strings from the file return new List<string>(); }
I've found plenty of examples of saving the file to the drive, but what if I instead want to skip this and just read the lines of text into an array in memory, directly from the IFormFile?
Inside the action method, the IFormFile contents are accessible as a Stream. So for IFormFile , you need to save it locally before using the local path. After this, you can get the local file path, namely filePath . In the case of a large file, isn't it taking a lot of time to get the file path after saving it locally?
What is IFormFile. ASP.NET Core has introduced an IFormFile interface that represents transmitted files in an HTTP request. The interface gives us access to metadata like ContentDisposition, ContentType, Length, FileName, and more. IFormFile also provides some methods used to store files.
The abstraction for the IFormFile
has an .OpenReadStream
method. To prevent a ton of undesirable and potentially large allocations, we should read a single line at a time and build up our list from each line that we read. Additionally, we could encapsulate this logic in an extension method. The Index
action ends up looking like this:
public List<string> Index(IFormFile file) => file.ReadAsList();
The corresponding extension method looks like this:
public static List<string> ReadAsList(this IFormFile file) { var result = new StringBuilder(); using (var reader = new StreamReader(file.OpenReadStream())) { while (reader.Peek() >= 0) result.AppendLine(reader.ReadLine()); } return result; }
Likewise you could have an async
version as well:
public static async Task<string> ReadAsStringAsync(this IFormFile file) { var result = new StringBuilder(); using (var reader = new StreamReader(file.OpenReadStream())) { while (reader.Peek() >= 0) result.AppendLine(await reader.ReadLineAsync()); } return result.ToString(); }
Alternatively, you could you use an ObjectPool<StringBuilder>
and modern C# 8 features.
public static async Task<string> ReadAsStringAsync( this IFormFile file, Object<StringBuilder> pool) { var builder = pool.Get(); try { using var reader = new StreamReader(file.OpenReadStream()); while (reader.Peek() >= 0) { builder.AppendLine(await reader.ReadLineAsync()); } return builder.ToString(); } finally { pool.Return(builder); } }
Then you could use this version this way:
public Task<List<string>> Index( IFormFile file, [FromServices] ObjectPool<StringBuilder> pool) => file.ReadAsListAsync(pool);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With