
Как прочитать текстовый файл в строку в ASP.NET
Шаблон письма, справочник, лог или конфиг: веб-приложению постоянно нужно читать текст с диска. Сравниваем способы от однострочника до потокового чтения.
Задача и откуда берётся путь
Классический сценарий: в папке App_Data лежит текстовый шаблон письма или список стоп-слов, и его нужно загрузить в строку, чтобы подставить значения или вывести на страницу. Сама операция чтения в .NET занимает одну строку кода, а основные грабли спрятаны вокруг неё: путь, кодировка и права доступа.
Веб-приложение не знает, в какой папке диска оно развёрнуто, и рассчитывать на текущий каталог процесса нельзя: для IIS это обычно system32. Поэтому виртуальный путь переводится в физический через Server.MapPath. Тильда в начале означает корень приложения, так код будет работать одинаково и на машине разработчика, и на сервере.
Папка App_Data выбрана не случайно. IIS не отдаёт её содержимое по прямой ссылке, поэтому туда можно класть служебные файлы, не опасаясь, что кто-то скачает их из браузера.
' Виртуальный путь превращаем в физический
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 недоступна без дополнительной настройки.
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 на границах буфера и не потребует считать байты вручную. Ниже вариант, который безопасно читает файл, даже если его в этот момент дописывает другой процесс (например, лог).
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Права, блокировки и кэширование
Код страницы выполняется от имени учётной записи пула приложений 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 оставьте для больших файлов, логов и случаев, когда важно контролировать режим доступа. Во всех вариантах явно думайте о кодировке: это главный источник ошибок при работе с русским текстом.
Как это делают сегодня
Вопросы и ответы
Почему Server.MapPath не работает в моём классе?
Объект Server доступен только внутри страницы или через HttpContext.Current. В библиотечном коде используйте HostingEnvironment.MapPath, он работает и вне контекста запроса.
Как узнать кодировку файла?
Надёжно только по BOM, если он есть. Без BOM кодировку можно лишь угадать, поэтому лучше договориться хранить все файлы в UTF-8 с BOM.
Можно ли читать файл, который пишет другой процесс?
Да, если открыть его через FileStream с FileShare.ReadWrite. Обычный File.ReadAllText в такой ситуации бросит IOException.