Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read into memory the lines of a text file from an IFormFile in ASP.NET Core?

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?

like image 802
MetaGuru Avatar asked Oct 14 '16 14:10

MetaGuru


People also ask

Where is the file path of 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 in asp net core?

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.


1 Answers

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); 
like image 57
David Pine Avatar answered Sep 17 '22 07:09

David Pine