Как считывать в память строки текстового файла из файла iform в ASP.NET Ядро?

#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();
    }
}