Как прочитать текстовый файл в строку в ASP.NET

РазделASP.NETУровеньНовичок Чтение7 минОпубликовано ТегиASP.NETVB.NETФайлы

Шаблон письма, справочник, лог или конфиг: веб-приложению постоянно нужно читать текст с диска. Сравниваем способы от однострочника до потокового чтения.

Главное. Прочитать файл в строку можно одной строкой, но путь стоит строить через MapPath, кодировку задавать явно, а для больших и занятых файлов использовать StreamReader с нужным режимом FileShare.

Задача и откуда берётся путь

Классический сценарий: в папке App_Data лежит текстовый шаблон письма или список стоп-слов, и его нужно загрузить в строку, чтобы подставить значения или вывести на страницу. Сама операция чтения в .NET занимает одну строку кода, а основные грабли спрятаны вокруг неё: путь, кодировка и права доступа.

Веб-приложение не знает, в какой папке диска оно развёрнуто, и рассчитывать на текущий каталог процесса нельзя: для IIS это обычно system32. Поэтому виртуальный путь переводится в физический через Server.MapPath. Тильда в начале означает корень приложения, так код будет работать одинаково и на машине разработчика, и на сервере.

Папка App_Data выбрана не случайно. IIS не отдаёт её содержимое по прямой ссылке, поэтому туда можно класть служебные файлы, не опасаясь, что кто-то скачает их из браузера.

Default.aspx.vbVB.NET
' Виртуальный путь превращаем в физический
Dim path As String = Server.MapPath("~/App_Data/template.txt")

' Вне страницы (в классе библиотеки) используем HostingEnvironment
Dim path2 As String = _
    System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/template.txt")

Способ 1: пространство имён My

В VB.NET есть удобная обёртка My.Computer.FileSystem. Метод ReadAllText открывает файл, читает его целиком и закрывает. Для небольших файлов этого достаточно, и код получается максимально читаемым.

Ограничение одно, но существенное: пространство имён My есть только в Visual Basic. Если часть команды пишет на C# или код планируется переносить в общую библиотеку, лучше сразу использовать классы System.IO, которые одинаково доступны из любого .NET-языка. Кроме того, в некоторых типах проектов (например, библиотеках классов) часть объектов My недоступна без дополнительной настройки.

Чтение через MyVB.NET
Dim text As String = My.Computer.FileSystem.ReadAllText(path)

' С явной кодировкой, если файл сохранён в Windows-1251
Dim text1251 As String = My.Computer.FileSystem.ReadAllText( _
    path, System.Text.Encoding.GetEncoding(1251))

Способ 2: File.ReadAllText

Класс System.IO.File делает то же самое и работает в любом языке. ReadAllText по умолчанию определяет кодировку по метке BOM в начале файла, а если её нет, считает текст UTF-8. Для файлов из старых Windows-систем это частый источник «кракозябр»: их нужно читать с явным указанием Windows-1251.

Если файл нужно обработать построчно, удобнее File.ReadAllLines, который сразу возвращает массив строк. Для больших файлов в .NET 4.0 появился File.ReadLines: он отдаёт строки по одной через перечисление и не держит весь файл в памяти.

Способ 3: поток и StreamReader

Ручное чтение через поток нужно, когда файл большой, читается частями или открыт другим процессом. В старых примерах часто встречается вариант с FileStream и массивом байтов, где байты потом декодируются в строку. У такого кода есть типичные ошибки: массив объявляется на байт длиннее файла (в VB верхняя граница массива включается), а результат Read игнорируется, из-за чего в строку попадает мусор.

Правильнее отдать декодирование StreamReader: он сам разберётся с BOM, правильно склеит многобайтовые символы UTF-8 на границах буфера и не потребует считать байты вручную. Ниже вариант, который безопасно читает файл, даже если его в этот момент дописывает другой процесс (например, лог).

TextReaderHelper.vbVB.NET
Imports System.IO
Imports System.Text

Public Module TextReaderHelper
    Public Function ReadText(path As String) As String
        ' FileShare.ReadWrite позволяет читать файл, открытый на запись
        Using fs As New FileStream(path, FileMode.Open, FileAccess.Read, _
                                   FileShare.ReadWrite)
            Using reader As New StreamReader(fs, Encoding.UTF8, True)
                Return reader.ReadToEnd()
            End Using
        End Using
    End Function
End Module
Третий параметр StreamReader (detectEncodingFromByteOrderMarks = True) говорит: если в начале файла есть BOM, верь ему, а не кодировке по умолчанию.

Права, блокировки и кэширование

Код страницы выполняется от имени учётной записи пула приложений IIS (например, IIS AppPool\MySite или NETWORK SERVICE в старых версиях). Если у неё нет прав на чтение папки, вы получите UnauthorizedAccessException, хотя из Проводника файл открывается без проблем. Права выдаются в свойствах папки, на вкладке «Безопасность».

Читать один и тот же файл с диска на каждый запрос расточительно. Если содержимое меняется редко, положите его в Cache с зависимостью CacheDependency от этого файла. ASP.NET сам сбросит кэш, когда файл изменится, а до тех пор строка будет браться из памяти.

  • FileNotFoundException: проверьте результат MapPath и регистр имени на сервере.
  • IOException «файл занят другим процессом»: используйте FileShare.ReadWrite.
  • Кракозябры вместо кириллицы: файл в Windows-1251, а читается как UTF-8.
  • Медленная страница: файл читается на каждый запрос, нужен кэш.

Что выбрать

Для коротких файлов в VB-проекте подойдёт любой однострочник, разница между My и File только в переносимости. Если код может переехать в C#, выбирайте File.ReadAllText. Потоковое чтение через StreamReader оставьте для больших файлов, логов и случаев, когда важно контролировать режим доступа. Во всех вариантах явно думайте о кодировке: это главный источник ошибок при работе с русским текстом.

Как это делают сегодня

В современном .NET читают асинхронно: File.ReadAllTextAsync не блокирует поток запроса во время ожидания диска. В ASP.NET Core путь к файлам берут из IWebHostEnvironment.ContentRootPath, а для отслеживания изменений используют IFileProvider с Watch. Справочные данные чаще кладут в appsettings.json или базу, а не в отдельные текстовые файлы.

Вопросы и ответы

Почему Server.MapPath не работает в моём классе?

Объект Server доступен только внутри страницы или через HttpContext.Current. В библиотечном коде используйте HostingEnvironment.MapPath, он работает и вне контекста запроса.

Как узнать кодировку файла?

Надёжно только по BOM, если он есть. Без BOM кодировку можно лишь угадать, поэтому лучше договориться хранить все файлы в UTF-8 с BOM.

Можно ли читать файл, который пишет другой процесс?

Да, если открыть его через FileStream с FileShare.ReadWrite. Обычный File.ReadAllText в такой ситуации бросит IOException.

Читайте также