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);
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…