Как открыть уже открытый файл с помощью .net StreamReader?

У меня есть некоторые .csv файлы, которые я использую как часть тестового стенда. Я могу открыть их и прочитать их без каких-либо проблем, если я уже не открыл файл в Excel, и в этом случае я получаю IOException:

System.IO.IOException: процесс не может получить доступ к файлу TestData.csv, потому что он используется другим процессом.

Это фрагмент из тестового стенда:

using (CsvReader csv = new CsvReader(new StreamReader(new FileStream(fullFilePath, FileMode.Open, FileAccess.Read)), false))
{
    // Process the file
}

Является ли это ограничением StreamReader? Я могу открыть файл в других приложениях (например, Notepad ++), поэтому он не может быть проблемой O/S. Может быть, мне нужно использовать какой-то другой класс? Если кто-нибудь знает, как я могу обойти это (помимо закрытия excel!), Я был бы очень благодарен.

Ответ 1

Как говорит Джаред, вы не можете этого сделать, если другой объект, который имеет открытый файл, не позволяет использовать общие чтения. Excel позволяет совместное чтение, даже для файлов, открытых для записи. Следовательно, вы должны открыть поток с параметром FileShare.ReadWrite.

Параметр FileShare часто неправильно понимается. Он указывает, что могут сделать другие открыватели файла. Это касается как прошлых, так и будущих разработчиков. Подумайте о том, что FileShare не является ретроактивным запретом на предыдущие открыватели (например, Excel), но ограничение, которое не должно быть нарушено с текущим Открытием или любым будущим Открывается.

В случае текущей попытки открыть файл FileShare.Read говорит, что "откройте этот файл для меня успешно, только если его открыли предыдущие открыватели только для чтения". Если вы укажете FileShare.Read в файле, который открыт для записи в Excel, ваш open будет терпеть неудачу, поскольку он будет нарушать ограничение, потому что Excel открывает его для записи.

Поскольку Excel имеет открытый файл для записи, вы должны открыть файл с помощью FileShare.ReadWrite, если вы хотите, чтобы ваше открытие было успешным. Еще один способ подумать о параметре FileShare: он указывает "доступ к файлу другого парня".

Теперь предположим другой сценарий, в котором вы открываете файл, который в настоящее время не открыт никаким другим приложением. FileShare.Read говорит, что "будущие openers могут открывать файл только с доступом для чтения".

Логически, эти семантики имеют смысл - FileShare.Read означает, что вы не хотите читать файл, если другой парень уже пишет его, и вы не хотите, чтобы другой парень записывал файл, если вы уже прочитав его. FileShare.ReadWrite означает, что вы готовы прочитать файл, даже если другой парень пишет его, и у вас нет проблем, позволяя другому открывающему писать файл во время его чтения.

Ни в коем случае это не допускает нескольких авторов. FileShare похож на базу данных IsolationLevel. Ваша желаемая настройка здесь зависит от требуемых "согласований".

Пример:

using (Stream s = new FileStream(fullFilePath, 
                                 FileMode.Open,
                                 FileAccess.Read,
                                 FileShare.ReadWrite))
{
  ...
}

или,

using (Stream s = System.IO.File.Open(fullFilePath, 
                                      FileMode.Open, 
                                      FileAccess.Read, 
                                      FileShare.ReadWrite))
{
}

Добавление:

Документация на System.IO.FileShare немного тонкая. Если вы хотите получить прямые факты, перейдите в документацию для функции CreateFile Win32, которая лучше объясняет концепцию FileShare.

Ответ 2

ИЗМЕНИТЬ

Я все еще не уверен на 100%, почему это ответ, но вы можете исправить эту проблему, передав FileShare.ReadWrite в конструктор FileStream.

using (CsvReader csv = new CsvReader(new StreamReader(new FileStream(fullFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)), false)
{
  ...
}

Мое любопытство держит меня в данный момент, и я пытаюсь понять, почему это конкретный ответ. Если я выясню это позже, я обновлю эту информацию.

Лучшая документация, по-видимому, находится в функции CreateFile. Это функция .Net вызывается под капотом, чтобы открыть файл (создать файл является немного неправильным). Он имеет лучшую документацию о том, как работает совлокальный аспект открытия файла. Другой вариант - просто прочитать ответ Чисо

Ответ 3

Если у другого процесса открыт файл, вы можете часто использовать File.Copy, а затем открыть копию. Не изящное решение, а прагматичное.

Ответ 4

Еще одна проблема заключается в том, что если вы открываете FileStream с FileShare.ReadWrite, последующие открытия этого файла должны также указывать FileShare.ReadWrite, или вы получите ошибку "Другой процесс использует этот файл".

Ответ 5

Использование System.Diagnostics;

Вы можете просто позвонить Process.Start( "имя_файла & Path" )

Не уверен, что это помогает, но вот то, что я только что использовал для реализации кнопки Preview PDF в нашей интрасети.