System.IO.IOException: “Tệp tồn tại” khi sử dụng System.IO.Path.GetTempFileName () - độ phân giải?


84

Một trong những khách hàng của tôi bị ngoại lệ bất cứ khi nào anh ta cố gắng sử dụng sản phẩm của tôi. Tôi đã nhận được callstack của ngoại lệ đã xảy ra, phần trên cùng của nó là:

at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.__Error.WinIOError()
   at System.IO.Path.GetTempFileName()
   at System.Windows.Input.Cursor.LoadFromStream(Stream cursorStream)
   at System.Windows.Input.Cursor..ctor(Stream cursorStream)

Trên Googling này, tôi thấy nhiều bài đăng trên blog nói rằng ngoại lệ này được đưa ra khi có hơn 65535 tệp tạm thời trong thư mục% TEMP% và giải pháp đơn giản là xóa các tệp tạm thời cũ. Tôi có thể yêu cầu khách hàng làm điều đó, nhưng đây có thể chỉ là giải pháp tạm thời - điều gì sẽ xảy ra nếu họ thường xuyên chạy một số phần mềm khác thực hiện các cuộc gọi thường xuyên đến GetTempFileName, điều này sẽ khiến sự cố lặp đi lặp lại?

Tôi không thể xóa thư mục% TEMP% theo chương trình, vì điều đó có thể làm hỏng thứ gì đó bằng cách nào đó và tôi không thể tránh gọi GetTempFileName (và thay vào đó là sử dụng thư mục tạm thời của riêng tôi) vì không phải tôi mà là mã WPF đang gọi nó.

Có giải pháp lâu dài nào cho việc này không?

CẬP NHẬT : Tôi đã xác nhận rằng sự cố trong đó thư mục% TEMP% tràn tệp nhật ký không phải do mã của riêng tôi gây ra và phải do một số ứng dụng bên thứ 3 khác trên máy của khách hàng gây ra. Tôi cũng đã xem xét việc triển khai Cursor.LoadFromStreamvà nó chắc chắn không có lỗi - nó tạo ra một tệp tạm thời, nhưng sau đó xóa nó trong finallykhối.


3
Bạn có thể làm của riêng "Temp" thư mục của bạn mà bị xóa (trong dữ liệu ứng dụng ") nhưng có lẽ sẽ là một ballache thay đổi tất cả tài liệu tham khảo, câu hỏi hay
— Sayse

Câu hỏi không liên quan đến WPF, đã xóa thẻ. Ngoài ra, tại sao chỉ không sửa mã, tạo ra rất nhiều tệp tạm thời mà không cần xóa?
— Dennis

1
@Sayse Tôi không thể làm điều đó vì WPF Cursor.LoadFromStreamđang tạo tệp tạm thời. @Dennis Nó liên quan đến Cursor.LoadFromStreamlớp của WPF . Mã vi phạm tạo ra rất nhiều tệp tạm thời mà không bị xóa thậm chí có thể không phải là của riêng tôi và tôi vẫn cần giải quyết ngoại lệ.
— Omer Raviv

Bạn có thể tìm ra ứng dụng nào đang để lại tất cả các tệp tạm thời này không? Nó có phải là ứng dụng của bạn? Nếu WPF đang tự tạo các tệp tạm thời này, bạn đã xác nhận rằng nó sẽ xóa chúng khi không còn cần thiết không?
— Ashigore

2
@OmerRaviv Tôi nghĩ lựa chọn duy nhất của bạn lúc đó là thử / bắt IOE và hỏi người dùng xem họ có muốn bạn xóa các tệp tạm thời không và thử lại
— Sayse

Câu trả lời:


15

Như tôi đã đề cập trong nhận xét cuối cùng của mình, tôi nghĩ rằng cách an toàn duy nhất của bạn để làm điều này là hỏi người dùng xem họ có muốn bạn xóa tệp hay không và thử lại. Điều bắt buộc là bạn phải thu hút được người dùng tham gia vào việc này, theo cách này, chính họ sẽ gặp nguy hiểm. Trong đầu tôi một cái gì đó tương tự như.

public Stream GetStream(Stream cursorStream)
{
    try
    {
       //getting stream
    }
    catch(IOE)
    {
        MessageBox.Show(this, "Unable to get stream, your temporary
                              folder may be full, do you want to try deleting 
                                some and try again?");
         if(yes)
         try
         {
             //delete and try again
             return GetStream(cursorStream);
         }
         catch(IOE)
          {
                //no luck
           }
          else
              return null;
    }

}

Một kiểm tra tùy chọn để đảm bảo có thể,

Directory.EnumerateFiles(Path.GetTempPath(), "*", SearchOption.TopLevelOnly)
  .Count() == ushort.MaxValue;

2
Tôi sẽ thử / bắt như bạn đề xuất. Kiểm tra boolean mà bạn đề xuất thực sự sai - có khả năng có các tệp khác trong thư mục% TEMP%, bên cạnh những tệp ở định dạng "tmpXXXX.tmp" mà GetTempFileName () sử dụng, vì vậy kiểm tra của bạn có thể trả về true khi thực sự không có vấn đề gì và sai khi có sự cố.
— Omer Raviv

Thử nghiệm được thiết kế để tìm tất cả các tệp trong một thư mục tạm thời (mặc dù tôi thừa nhận rằng nó có thể không phải là thư mục tạm thời phù hợp) và xem nếu điều đó bằng 65535, "*"sẽ tìm thấy tất cả các tệp (bất kể chúng có phần mở rộng hay không) hy vọng nó sẽ giúp
— Sayse

Bạn có thể xóa tất cả các tệp cũ hơn, chẳng hạn như một ngày nào đó. Một tệp tạm thời cũ hơn một ngày sẽ không được sử dụng bởi bất kỳ ứng dụng nào khác.
— JT Taylor,

2
Chỉ là một điểm khác trên séc tùy chọn. Khi tôi gặp sự cố này trên máy chủ xây dựng, thư mục tạm thời có hơn 65535 tệp. Số lượng đó chỉ hoạt động nếu điều DUY NHẤT tạo tệp tạm thời là thông qua lớp trợ giúp Đường dẫn.
— rshadman

36

Nếu điều này xảy ra với bạn trên môi trường sản xuất hoặc với một ứng dụng mà bạn không thể thay đổi, thì cách khắc phục nhanh chóng là làm trống thư mục Temp.

Tùy thuộc vào người dùng đang chạy ứng dụng, bạn nên

  • Rỗng C:\Windows\Temp(đối với IIS hoặc các dịch vụ đang chạy trong LocalSystemtài khoản)
  • Hoặc %temp%cho người dùng đã đăng nhập cục bộ (đối với tôi là như vậy C:\Users\MyUserName\AppData\Local\Temp).

Mặt khác, nếu mã của riêng bạn đang ném điều này và bạn muốn ngăn điều này xảy ra lần nữa:

  1. Không sử dụng System.IO.Path.GetTempFileName ()!

GetTempFileName()là một trình bao bọc của Win32 Api đã hai thập kỷ . Nó tạo ra các tên tệp sẽ rất dễ xảy ra xung đột. Nó phá vỡ các đối chiếu đó bằng cách lặp lại nhiều lần trên hệ thống tệp, lặp lại các tên tệp có thể từ "%temp%\tmp0000.tmp"đến "tmpFFFF.tmp"và bỏ qua các tên đã có. Đây là một thuật toán I / O chuyên sâu, chậm và thực sự khủng khiếp. Ngoài ra, chỉ sử dụng 4 ký tự hex là điều tạo nên giới hạn giả tạo của tệp 65536 trước khi không thành công.

Giải pháp thay thế là tạo các tên tệp không đụng nhau. Ví dụ, cho phép sử dụng lại GUID'slogic: 32 chữ số hex hầu như sẽ không bao giờ va chạm.

private string GetTempFileName()
{
    return Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
}
// Sample: c:\Windows\Temp\2e38fe87-f6bb-4b0d-90b3-2d07016324c1

Điều này mở rộng giới hạn từ 65k lên tối đa 4k triệu tệp (về mặt lý thuyết) ... Tất nhiên, việc để rò rỉ 65k tệp đã là khủng khiếp, vì vậy ...

  1. Không làm rò rỉ các tệp tạm thời!

Kiểm tra kỹ ứng dụng của bạn để biết tất cả các con đường hạnh phúc và không hạnh phúc (như các trường hợp ngoại lệ không mong muốn). Đảm bảo nó sắp xếp chính xác từng FileStream và xóa các tệp tạm thời trong các khối Cuối cùng.

  1. Làm sạch thư mục tạm thời

Làm sạch nó ngay bây giờ và yêu cầu quản trị viên hệ thống làm sạch nó định kỳ, vì bạn không thể tin tưởng mọi ứng dụng trong tự nhiên. Trên máy chủ của riêng tôi, tôi sẽ tự động hóa tác vụ này bằng cách sử dụng:

  • Đối với Windows toàn cầu \ Temp

schtasks /Create /TR "cmd /c call DEL /F /S /Q %^TEMP%" /TN "Delete Global Temp Files" /sc WEEKLY /ST 12:00 /ru system

  • Đối với người dùng hiện tại:

schtasks /Create /TR "cmd /c call DEL /F /S /Q %^TEMP%" /TN "Delete %username% Temp Files" /sc WEEKLY /ST 12:00


5

Đây là mã cuối cùng tôi đã sử dụng và đặt sớm vào đường dẫn mã khởi tạo ứng dụng của tôi, trước khi bất kỳ lệnh gọi nào Cursor.LoadFromStreamcó thể xảy ra:

    private void WarnUserIfTempFolderFull()
    {
        string tempFile = null;
        try
        {
            tempFile = Path.GetTempFileName();
        }
        catch (IOException e)
        {
            string problem = "The Temporary Folder is full.";

            string message = "{ProductName} has detected that the Windows Temporary Folder is full. \n" + 
                             "This may prevent the {ProductName} from functioning correctly.\n" + 
                             "Please delete old files in your temporary folder (%TEMP%) and try again.";

            Logger.Warn(problem);

            MessageBox.Show(message, caption: problem);
        }
        finally
        {
            if (tempFile != null) File.Delete(tempFile);
        }
    }

2

Các giải pháp:

  1. Một trong những quyền. Phát hiện ứng dụng nào đang tạo ra quá nhiều tệp tạm thời và không xóa chúng. Các tiện ích nhưProcess monitor sẽ giúp bạn. Sau đó, sửa ứng dụng hoặc vứt nó đi. Và có, đây có thể là ứng dụng của bạn. đó là lý do tại sao tôi khuyên bạn nên phát hiện ra nguồn gốc của cái ác.
  2. Đơn giản nhất. Sử dụng thư mục tạm thời của riêng bạn. Điều này sẽ không hữu ích nếu các tệp đang được tạo từ mã của bạn.
  3. Cái xấu xí nhất. Xóa thư mục tạm thời khỏi ứng dụng của bạn. Bạn hoàn toàn đúng về hậu quả - bạn có thể phá vỡ một ứng dụng khác.

Sử dụng thư mục tạm thời của riêng bạn không nhất thiết là một giải pháp. Tôi sử dụng API để tạo tên tệp tạm thời nhưng ghi nó vào thư mục của riêng tôi. Thật không may ngay cả ĐÓ không thành công.
— George Mauer

2
// one more implementation
string GetTempFileName()
{
    return Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
}

1

Như Sayse đã đề xuất, bạn có thể thử đặt biến môi trường% TEMP% khi ứng dụng của bạn khởi chạy.

Environment.SetEnvironmentVariable("TEMP", "<dir>");

Đó là một ý tưởng tuyệt vời, nhưng rất tiếc, ứng dụng của tôi là tiện ích mở rộng Visual Studio phải cùng tồn tại một cách hòa bình với các tiện ích mở rộng khác và tôi e rằng điều này có thể vô tình làm hỏng hoạt động của các tiện ích mở rộng khác.
— Omer Raviv

Giải pháp này sẽ không hữu ích nếu đó là chương trình của riêng anh ta đang bỏ lại tất cả các tệp này. Thư mục mới sẽ chỉ đầy và thật tệ khi tự ý xóa các tệp khỏi một thư mục khi bạn không biết chúng dùng để làm gì ngay cả khi đó là một thư mục dành riêng cho ứng dụng của bạn.
— Ashigore

@Ashigore Vâng, rõ ràng là điều này sẽ không sửa được lỗi mà anh ấy đã tạo. Tham khảo cụ thể what if they are regularly running some other piece of software that makes frequent calls to GetTempFileName.
— Ed Chapel

@OmerRaviv Thông tin này hữu ích. Thật vậy, điều này sẽ không hoạt động trong trường hợp này.
— Ed Chapel

1
Xin lỗi @EdChapel, SO đã khóa phiếu bầu của tôi, tôi không thể xóa nó trừ khi câu trả lời được chỉnh sửa.
— Gerardo Grignoli,

1

Đối với bất kỳ ai khác đã gặp sự cố này và không thể tìm thấy bất kỳ thư mục tạm thời tràn nào - Hãy kiểm tra thư mục "C: / Windows / Temp". Làm sạch thư mục này đã giải quyết được vấn đề của tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.