小技巧-自動刪除App_Data過期暫存檔
5 |
寫網站時有時需要產生暫存檔,過去我慣用的做法是透過System.IO.Path.GetTempPath(),將暫存檔寫入Windows系統暫存資料夾,如意算盤是暫存資料夾本來就是放暫存檔的地方,而Windows有機制可在磁碟空間不足時刪除暫存檔釋放空間,如果檔案沒有機密或敏感性,放在暫存資料夾基本上可以Fire and Forget,用過就放著不管,很省事。
後來幾次部署經驗告訴我,「ASP.NET程式不一定有寫入暫存資料夾的權限」(登楞!)。針對Temp資料夾開放ASP.NET寫入權限是一種解法,但每個ASP.NET應用程式的執行身分不同,搞下來Temp資料夾需維護一堆權限有點複雜。相形之下,App_Data隸屬各ASP.NET應用程式,可部署時一併設定權限統一管理,相對簡便。(註:適合放暫存檔是因為App_Data具有隱身特性,請不要將暫存檔放在一般網站目錄下,以免被使用者不當存取)
但暫存檔放App_Data就不像系統暫存資料夾可以靠Windows機制回收空間,得自行清理過期暫存檔。最直覺做法是寫個排程每天定時刪檔,但最近我想出另一個更省事的做法-取得暫存目錄時自動清理過期檔案。程式範例如下,分享兼請大家Code Review:P
//記憶上次清理執行日期 static string lastAttTempCleanDate = null; //非同步鎖定 static object cleanLock = new Object(); static string GetTempPath() { //使用App_Data暫存檔案,避免ASPX沒有寫入系統TEMP權限 string tempPath = Path.Combine( HttpContext.Current.Server.MapPath("~/App_Data"), "UploadAtts"); //若目錄不存在,建立之 Directory.CreateDirectory(tempPath); //每次啟動網站或每天清理一次過期Cache string todayDate = DateTime.Today.ToString("yyyyMMdd"); //防止多執行緒同時觸發 lock (cleanLock) { //比對上次執行日期,原則上每天只執行一次 if (todayDate != lastAttTempCleanDate) { //先儲存執行日期記錄,避免重複執行 lastAttTempCleanDate = todayDate; //另開Thread執行刪除,以免卡住目前的暫存作業 Task.Factory.StartNew(() => { //決定逾期判斷基準 DateTime expireTime = DateTime.Today; foreach (var file in Directory.GetFiles(tempPath)) { //若檔案建立時間早於今天,刪除之 if (File.GetCreationTime(file).CompareTo(expireTime) == -1) { File.Delete(file); } } }); } } return tempPath; } |
Comments
# by 毛豆
但暫存檔放App_Data就像系統暫存資料夾可以靠Windows機制回收空間 (X) 但暫存檔放App_Data就不像系統暫存資料夾可以靠Windows機制回收空間 (O)
# by Jeffrey
謝謝毛豆,巧妙地由指間流洩出各式錯字漏詞倒序亂排一定是一種天賦(遠目)可是我一點都不想要啦~ orz
# by Slash
建議考慮將lock移到task內會比較好,因為當task運行比較久時(一大堆檔案待砍),本身這個thread早已經release掉了(尤其好發於async Task),那麼下一個程式碼中再調用GetTempPath()方法時依然不會被lock到,lock意義就顯得很淡了。
# by Jeffrey
to Slash, lock的目的在保護單一時間只能有一個Thread執行todayDate != lastAttTempCleanDate比對,避免同時兩條Thread判定todayDate != lastAttTempCleanDate而觸發兩個Task起來砍檔。 砍檔Task執行需要一段時間,但由於執行前已lastAttTempCleanDate = todayDate,不會再觸發第二個砍檔Task。若lock放在Task中,有可能同時觸發n個砍檔Task,第1個Task執行,第2-n個Task等待第1個Task執行完,又輪番重掃一次檔案(但逾時檔案已被第一個Task砍光)空耗效能。 希望這樣有解釋將lock放在外圍的用意。
# by Slash
嗯,你說的沒錯,如果將Lock移到Task內的確反而會引發這方面的問題,我了解你的設計方式了。 另外建議,你用static來存資料很By server,如果顧慮到AP重開機、或者在LB的共用檔案目錄的環境下,我會選擇做一個自己可視別的小TXT檔,丟在檔案目錄內來做為File.Exists參考。 畢竟,他都是垃圾桶了,剩一個接近0K的小檔案無妨,哈哈!