Piszę program w języku C #, który musi wielokrotnie uzyskiwać dostęp do 1 pliku obrazu. Przez większość czasu działa, ale jeśli mój komputer działa szybko, spróbuje uzyskać dostęp do pliku, zanim zostanie zapisany z powrotem w systemie plików, i zgłosi błąd: „Plik używany przez inny proces” .
Chciałbym znaleźć sposób na obejście tego, ale cały mój Googling dał tylko tworzenie czeków przy użyciu obsługi wyjątków. To jest sprzeczne z moją religią, więc zastanawiałem się, czy ktoś ma lepszy sposób na zrobienie tego?
Odpowiedzi:
Zaktualizowana UWAGA dotycząca tego rozwiązania : Sprawdzanie za pomocą
FileAccess.ReadWrite
nie powiedzie się w przypadku plików tylko do odczytu, więc rozwiązanie zostało zmodyfikowane w celu sprawdzania za pomocąFileAccess.Read
. Chociaż to rozwiązanie działa, ponieważ próba sprawdzenia za pomocąFileAccess.Read
nie powiedzie się, jeśli plik ma blokadę zapisu lub odczytu, jednak to rozwiązanie nie będzie działać, jeśli plik nie ma blokady zapisu lub odczytu, tj. Został otwarty (do odczytu lub zapisu) za pomocą dostępu FileShare.Read lub FileShare.Write.ORYGINALNY: Używam tego kodu od kilku lat i nie miałem z nim żadnych problemów.
Poznaj swoje wahanie dotyczące korzystania z wyjątków, ale nie możesz ich cały czas unikać:
źródło
public static bool IsLocked(this FileInfo file) {/*...*/}
.Możesz cierpieć na warunek wyścigu wątków, który jest udokumentowany, przykłady wykorzystania tego jako luki w zabezpieczeniach. Jeśli sprawdzisz, czy plik jest dostępny, ale następnie spróbujesz go użyć, możesz w tym momencie rzucić, którego złośliwy użytkownik może użyć do wymuszenia i wykorzystania w kodzie.
Twój najlepszy zakład to try catch / wreszcie, który próbuje uzyskać uchwyt pliku.
źródło
Użyj tego, aby sprawdzić, czy plik jest zablokowany:
Ze względu na wydajność zalecam przeczytanie zawartości pliku podczas tej samej operacji. Oto kilka przykładów:
Wypróbuj sam:
źródło
IOException
, zamiast ogólnie,Exception
a następnie test typu.IOException
po ogólnym. Ogólny złapie wszystko, co przechodzi, a konkretnyIOException
zawsze będzie samotny. Po prostu zamień dwa.Po prostu użyj wyjątku zgodnie z przeznaczeniem. Zaakceptuj, że plik jest w użyciu i spróbuj ponownie, aż do zakończenia działania. Jest to również najbardziej wydajne, ponieważ nie marnujesz żadnych cykli sprawdzania stanu przed działaniem.
Użyj na przykład poniższej funkcji
Metoda wielokrotnego użytku, która kończy się po 2 sekundach
źródło
Być może możesz użyć FileSystemWatcher i obserwować wydarzenie Zmienione.
Nie użyłem tego sam, ale może warto spróbować. Jeśli obserwator systemu plików okaże się nieco ciężki w tym przypadku, wybrałbym pętlę try / catch / sleep.
źródło
Możesz zwrócić zadanie, które daje strumień, gdy tylko będzie dostępny. To uproszczone rozwiązanie, ale dobry punkt wyjścia. Jest bezpieczny dla wątków.
Możesz użyć tego strumienia jak zwykle:
źródło
GetStreamAsync()
?jedynym sposobem, jaki znam, jest użycie wyjątkowego interfejsu API blokady Win32, który nie jest zbyt szybki, ale istnieją przykłady.
Większość ludzi, dla prostego rozwiązania tego problemu, po prostu próbuje / łapie / śpi pętle.
źródło
Mam nadzieję że to pomoże!
źródło
W powyższych odpowiedziach występuje problem polegający na tym, że jeśli plik został otwarty do zapisu w trybie FileShare.Read lub jeśli plik ma atrybut tylko do odczytu, kod nie będzie działał. To zmodyfikowane rozwiązanie działa najbardziej niezawodnie, pamiętając o dwóch rzeczach (tak jak w przypadku przyjętego rozwiązania):
Mając powyższe na uwadze, sprawdza, czy plik jest zablokowany do zapisu lub zablokowany, aby uniemożliwić odczyt :
źródło
Poza działającymi 3 liniami i tylko w celach informacyjnych: Jeśli chcesz uzyskać pełne informacje - istnieje mały projekt w Microsoft Dev Center:
https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-704839f4
Ze wstępu:
Działa poprzez połączenie z „Restart Manager Session”.
To może być trochę overengineered do indywidualnych potrzeb ... Ale jeśli to, co ty chcesz, idź i chwyć VS-projekt.
źródło
Z mojego doświadczenia wynika, że zwykle chcesz to zrobić, a następnie „chronić” swoje pliki, aby zrobić coś wymyślnego, a następnie użyć plików „chronionych”. Jeśli masz tylko jeden plik, którego chcesz użyć w ten sposób, możesz skorzystać ze sztuczki wyjaśnionej w odpowiedzi przez Jeremy'ego Thompsona. Jeśli jednak spróbujesz to zrobić na wielu plikach (powiedzmy na przykład podczas pisania instalatora), czeka cię sporo bólu.
Bardzo eleganckim sposobem na rozwiązanie tego problemu jest fakt, że twój system plików nie pozwoli ci zmienić nazwy folderu, jeśli jeden z plików tam używanych jest używany. Trzymaj folder w tym samym systemie plików, a będzie działał jak urok.
Pamiętaj, że powinieneś być świadomy oczywistych sposobów, w jakie można to wykorzystać. W końcu pliki nie zostaną zablokowane. Należy również pamiętać, że istnieją inne powody, które mogą spowodować
Move
niepowodzenie operacji. Oczywiście może pomóc tutaj poprawna obsługa błędów (MSDN).W przypadku pojedynczych plików trzymałbym się sugestii blokowania opublikowanej przez Jeremy'ego Thompsona.
źródło
FileShare
i sprawdzania zamka.Oto kod, który, o ile mogę najlepiej powiedzieć, robi to samo co zaakceptowana odpowiedź, ale z mniejszym kodem:
Myślę jednak, że bardziej niezawodne jest to zrobić w następujący sposób:
źródło
Możesz użyć mojej biblioteki do uzyskiwania dostępu do plików z wielu aplikacji.
Możesz go zainstalować z nuget: Install-Package Xabe.FileLock
Jeśli chcesz uzyskać więcej informacji na ten temat, sprawdź https://github.com/tomaszzmuda/Xabe.FileLock
Metoda fileLock.Acquire zwróci wartość true tylko wtedy, gdy można zablokować plik wyłączny dla tego obiektu. Ale aplikacja, która przesyła plik, musi to zrobić również w blokadzie plików. Jeśli obiekt jest niedostępny, metoda zwraca false.
źródło
Kiedyś musiałem przesyłać pliki PDF do archiwum kopii zapasowych online. Ale tworzenie kopii zapasowej nie powiedzie się, jeśli użytkownik otworzy plik w innym programie (np. Czytniku plików PDF). W pośpiechu spróbowałem kilka najlepszych odpowiedzi w tym wątku, ale nie udało mi się ich uruchomić. To, co zadziałało, to próba przeniesienia pliku PDF do własnego katalogu . Odkryłem, że to się nie powiedzie, jeśli plik zostanie otwarty w innym programie, a jeśli przeniesienie się powiedzie, operacja przywracania nie będzie wymagana, tak jak w przypadku przeniesienia do osobnego katalogu. Chcę opublikować moje podstawowe rozwiązanie na wypadek, gdyby okazało się przydatne w konkretnych przypadkach użycia przez innych.
źródło
Interesuje mnie, czy to uruchomi jakiś refleks WTF. Mam proces, który tworzy, a następnie uruchamia dokument PDF z aplikacji konsoli. Miałem jednak do czynienia z słabością, w której jeśli użytkownik miałby uruchomić proces wiele razy, generując ten sam plik bez uprzedniego zamknięcia wcześniej wygenerowanego pliku, aplikacja rzuciłaby wyjątek i zginęła. Było to dość częste zdarzenie, ponieważ nazwy plików oparte są na numerach ofert sprzedaży.
Zamiast zawieść w tak nieskromny sposób, postanowiłem polegać na automatycznie zwiększanej wersji plików:
Prawdopodobnie można poświęcić trochę więcej uwagi
catch
blokowi, aby upewnić się, że wychwytuję poprawne wyjątki IOException. Prawdopodobnie wyczyszczę również pamięć aplikacji podczas uruchamiania, ponieważ te pliki i tak mają być tymczasowe.Zdaję sobie sprawę, że wykracza to poza kwestię PO polegającą na sprawdzeniu, czy plik jest w użyciu, ale to był problem, który chciałem rozwiązać, kiedy tu przybyłem, więc być może przyda się komuś innemu.
źródło
Czy coś takiego mogłoby pomóc?
źródło
Spróbuj przenieść / skopiować plik do katalogu tymczasowego. Jeśli możesz, nie ma blokady i możesz bezpiecznie pracować w temp temp bez uzyskiwania blokad. W przeciwnym razie spróbuj przenieść go ponownie za x sekund.
źródło
Korzystam z tego obejścia, ale mam pewien przedział czasowy między momentem sprawdzenia blokady pliku za pomocą funkcji IsFileLocked a otwarciem pliku. W tym czasie jakiś inny wątek może otworzyć plik, więc otrzymam wyjątek IOException.
Dodałem do tego dodatkowy kod. W moim przypadku chcę załadować XDocument:
Co myślisz? Czy mogę coś zmienić? Może wcale nie musiałem używać funkcji IsFileBeingUsed?
Dzięki
źródło