#parsing #file-upload #asp.net-core
#синтаксический анализ #загрузка файла #asp.net-ядро
Вопрос:
Допустим, у вас есть это действие:
public List<string> Index(IFormFile file){
//extract list of strings from the file
return new List<string>();
}
Я нашел множество примеров сохранения файла на диск, но что, если я вместо этого хочу пропустить это и просто прочитать строки текста в массив в памяти непосредственно из файла IFormFile?
Ответ №1:
Абстракция для IFormFile
имеет .OpenReadStream
метод. Чтобы предотвратить массу нежелательных и потенциально больших выделений, мы должны читать по одной строке за раз и создавать наш список из каждой прочитанной строки. Кроме того, мы могли бы инкапсулировать эту логику в метод расширения. Index
Действие в конечном итоге выглядит так:
public List<string> Index(IFormFile file) => file.ReadAsList();
Соответствующий метод расширения выглядит следующим образом:
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 resu<
}
Аналогично, у вас async
также может быть версия:
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();
}
В качестве альтернативы, вы могли бы использовать an ObjectPool<StringBuilder>
и современные функции C # 8.
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);
}
}
Тогда вы могли бы использовать эту версию таким образом:
public Task<List<string>> Index(
IFormFile file, [FromServices] ObjectPool<StringBuilder> pool) =>
file.ReadAsListAsync(pool);
Комментарии:
1. это сработало, хотя оно не принимало Environment.NewLine, поэтому я использовал
Split('n')
, что-то о желании использовать символ вместо строки2. Зачем вам читать все это в одну строку, а затем разделять, а затем составлять список? Почему бы просто не прочитать его построчно и не добавить в список? Приведенный выше код выделяет потенциально гигантскую строку, затем выделяет массив, а затем выделяет список.
3. Отличное наблюдение. Я просто показывал, как это можно сделать, я обновлю код — спасибо.
4. @davidfowl лучше, или что еще вы бы предложили?
5. Намного лучше. Сборщик мусора благодарит вас. Чтобы работать еще лучше, нам нужно было бы знать, что потребитель делал с этими строками. Может быть, им никогда не нужно быть в списке одновременно.
Ответ №2:
ASP.NET Ядро 3.0 — чтение содержимого файла формы в строку
public static async Task<string> ReadFormFileAsync(IFormFile file)
{
if (file == null || file.Length == 0)
{
return await Task.FromResult((string)null);
}
using (var reader = new StreamReader(file.OpenReadStream()))
{
return await reader.ReadToEndAsync();
}
}