1 이미지 파일에 반복적으로 액세스 해야하는 프로그램을 C #으로 작성 중입니다. 대부분의 경우 작동하지만 내 컴퓨터가 빠르게 실행되는 경우 파일 시스템에 다시 저장하기 전에 파일에 액세스하려고 시도하고 “다른 프로세스에서 사용중인 파일” 오류가 발생 합니다.
이 문제를 해결할 방법을 찾고 싶지만 내 모든 인터넷 검색은 예외 처리를 사용하여 검사를 만들었습니다. 이것은 내 종교에 위배되므로 누군가 더 좋은 방법이 있는지 궁금합니다.
답변
이 솔루션에 대한 업데이트 된 참고 : FileAccess.ReadWrite
파일을 검사 하면 읽기 전용 파일이 검사 되지 않으므로 솔루션을 검사하도록 수정되었습니다 FileAccess.Read
. FileAccess.Read
파일에 쓰기 또는 읽기 잠금이있는 경우 확인 시도 가 실패 하기 때문에이 솔루션이 작동하지만 파일에 쓰기 또는 읽기 잠금이없는 경우 (예 : 파일이 열려있는 경우)이 솔루션이 작동하지 않습니다. FileShare.Read 또는 FileShare.Write 액세스 권한으로 (읽기 또는 쓰기 용)
ORIGINAL :
지난 몇 년 동안이 코드를 사용해 왔으며 아무런 문제가 없었습니다.
예외 사용에 대한 망설임을 이해하지만 항상 예외를 피할 수는 없습니다.
protected virtual bool IsFileLocked(FileInfo file)
{
try
{
using(FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
{
stream.Close();
}
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
return true;
}
//file is not locked
return false;
}
답변
보안 취약점으로 사용되는 문서화 된 예제가있는 스레드 경쟁 조건이 발생할 수 있습니다. 파일이 사용 가능한지 확인한 다음 파일을 사용하여 사용하려고하면 악의적 인 사용자가 코드를 강제로 이용하고 악용 할 수있는 시점에이를 던질 수 있습니다.
가장 좋은 방법은 try catch / finally 파일 핸들을 얻는 것입니다.
try
{
using (Stream stream = new FileStream("MyFilename.txt", FileMode.Open))
{
// File/Stream manipulating code here
}
} catch {
//check here why it failed and ask user to retry if the file is in use.
}
답변
이를 사용하여 파일이 잠겨 있는지 확인하십시오.
using System.IO;
using System.Runtime.InteropServices;
internal static class Helper
{
const int ERROR_SHARING_VIOLATION = 32;
const int ERROR_LOCK_VIOLATION = 33;
private static bool IsFileLocked(Exception exception)
{
int errorCode = Marshal.GetHRForException(exception) & ((1 << 16) - 1);
return errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION;
}
internal static bool CanReadFile(string filePath)
{
//Try-Catch so we dont crash the program and can check the exception
try {
//The "using" is important because FileStream implements IDisposable and
//"using" will avoid a heap exhaustion situation when too many handles
//are left undisposed.
using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) {
if (fileStream != null) fileStream.Close(); //This line is me being overly cautious, fileStream will never be null unless an exception occurs... and I know the "using" does it but its helpful to be explicit - especially when we encounter errors - at least for me anyway!
}
}
catch (IOException ex) {
//THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
if (IsFileLocked(ex)) {
// do something, eg File.Copy or present the user with a MsgBox - I do not recommend Killing the process that is locking the file
return false;
}
}
finally
{ }
return true;
}
}
성능상의 이유로 동일한 작업으로 파일 내용을 읽는 것이 좋습니다. 여기 몇 가지 예가 있어요.
public static byte[] ReadFileBytes(string filePath)
{
byte[] buffer = null;
try
{
using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
int length = (int)fileStream.Length; // get file length
buffer = new byte[length]; // create buffer
int count; // actual number of bytes read
int sum = 0; // total number of bytes read
// read until Read method returns 0 (end of the stream has been reached)
while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
sum += count; // sum is a buffer offset for next reading
fileStream.Close(); //This is not needed, just me being paranoid and explicitly releasing resources ASAP
}
}
catch (IOException ex)
{
//THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
if (IsFileLocked(ex))
{
// do something?
}
}
catch (Exception ex)
{
}
finally
{
}
return buffer;
}
public static string ReadFileTextWithEncoding(string filePath)
{
string fileContents = string.Empty;
byte[] buffer;
try
{
using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
int length = (int)fileStream.Length; // get file length
buffer = new byte[length]; // create buffer
int count; // actual number of bytes read
int sum = 0; // total number of bytes read
// read until Read method returns 0 (end of the stream has been reached)
while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
{
sum += count; // sum is a buffer offset for next reading
}
fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP
//Depending on the encoding you wish to use - I'll leave that up to you
fileContents = System.Text.Encoding.Default.GetString(buffer);
}
}
catch (IOException ex)
{
//THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
if (IsFileLocked(ex))
{
// do something?
}
}
catch (Exception ex)
{
}
finally
{ }
return fileContents;
}
public static string ReadFileTextNoEncoding(string filePath)
{
string fileContents = string.Empty;
byte[] buffer;
try
{
using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
int length = (int)fileStream.Length; // get file length
buffer = new byte[length]; // create buffer
int count; // actual number of bytes read
int sum = 0; // total number of bytes read
// read until Read method returns 0 (end of the stream has been reached)
while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
{
sum += count; // sum is a buffer offset for next reading
}
fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP
char[] chars = new char[buffer.Length / sizeof(char) + 1];
System.Buffer.BlockCopy(buffer, 0, chars, 0, buffer.Length);
fileContents = new string(chars);
}
}
catch (IOException ex)
{
//THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
if (IsFileLocked(ex))
{
// do something?
}
}
catch (Exception ex)
{
}
finally
{
}
return fileContents;
}
직접 해보십시오.
byte[] output1 = Helper.ReadFileBytes(@"c:\temp\test.txt");
string output2 = Helper.ReadFileTextWithEncoding(@"c:\temp\test.txt");
string output3 = Helper.ReadFileTextNoEncoding(@"c:\temp\test.txt");
답변
의도 한대로 예외를 사용하십시오. 파일이 사용 중임을 승인하고 조치가 완료 될 때까지 반복하여 다시 시도하십시오. 또한 작동하기 전에 상태를 확인하는주기를 낭비하지 않기 때문에 가장 효율적입니다.
예를 들어 아래 기능을 사용하십시오.
TimeoutFileAction(() => { System.IO.File.etc...; return null; } );
2 초 후에 시간 초과되는 재사용 가능한 방법
private T TimeoutFileAction<T>(Func<T> func)
{
var started = DateTime.UtcNow;
while ((DateTime.UtcNow - started).TotalMilliseconds < 2000)
{
try
{
return func();
}
catch (System.IO.IOException exception)
{
//ignore, or log somewhere if you want to
}
}
return default(T);
}
답변
아마도 FileSystemWatcher를 사용 하여 Changed 이벤트를 감시 할 수 있습니다 .
나는 이것을 직접 사용하지는 않았지만 기회가 될 수도 있습니다. 이 경우 파일 시스템 감시자가 약간 무겁다면 try / catch / sleep 루프를 사용합니다.
답변
스트림이 제공되는 즉시 스트림을 제공하는 작업을 반환 할 수 있습니다. 간단한 솔루션이지만 좋은 출발점입니다. 스레드 안전합니다.
private async Task<Stream> GetStreamAsync()
{
try
{
return new FileStream("sample.mp3", FileMode.Open, FileAccess.Write);
}
catch (IOException)
{
await Task.Delay(TimeSpan.FromSeconds(1));
return await GetStreamAsync();
}
}
평소처럼이 스트림을 사용할 수 있습니다.
using (var stream = await FileStreamGetter.GetStreamAsync())
{
Console.WriteLine(stream.Length);
}
답변
내가 아는 유일한 방법은 너무 빠르지 않지만 예제가있는 Win32 독점 잠금 API를 사용하는 것입니다.
대부분의 사람들은 이것에 대한 간단한 해결책을 위해 단순히 루프를 시도 / 잡기 / 수면합니다.