ASP.NET 计划删除临时文件

Posted

技术标签:

【中文标题】ASP.NET 计划删除临时文件【英文标题】:ASP.NET Schedule deletion of temporary files 【发布时间】:2011-02-19 10:53:30 【问题描述】:

问题:我有一个创建临时 PDF 文件(供用户下载)的 ASP.NET 应用程序。 现在,很多用户可以在很多天里创建很多 PDF,这会占用很多磁盘空间。

安排删除超过 1 天/8 小时的文件的最佳方式是什么? 最好在asp.net应用程序本身...

【问题讨论】:

【参考方案1】:

使用缓存过期通知触发文件删除:

    private static void DeleteLater(string path)
    
        HttpContext.Current.Cache.Add(path, path, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, 8, 0, 0), CacheItemPriority.NotRemovable, UploadedFileCacheCallback);
    

    private static void UploadedFileCacheCallback(string key, object value, CacheItemRemovedReason reason)
    
        var path = (string) value;
        Debug.WriteLine(string.Format("Deleting upladed file '0'", path));
        File.Delete(path);
    

参考:MSDN | How to: Notify an Application When an Item Is Removed from the Cache

【讨论】:

【参考方案2】:

对于您需要创建的每个临时文件,记下会话中的文件名:

// create temporary file:
string fileName = System.IO.Path.GetTempFileName();
Session[string.Concat("temporaryFile", Guid.NewGuid().ToString("d"))] = fileName;
// TODO: write to file

接下来,将以下清理代码添加到 global.asax:

<%@ Application Language="C#" %>
<script RunAt="server">
    void Session_End(object sender, EventArgs e) 
        // Code that runs when a session ends. 
        // Note: The Session_End event is raised only when the sessionstate mode
        // is set to InProc in the Web.config file. If session mode is set to StateServer 
        // or SQLServer, the event is not raised.

        // remove files that has been uploaded, but not actively 'saved' or 'canceled' by the user
        foreach (string key in Session.Keys) 
            if (key.StartsWith("temporaryFile", StringComparison.OrdinalIgnoreCase)) 
                try 
                    string fileName = (string)Session[key];
                    Session[key] = string.Empty;
                    if ((fileName.Length > 0) && (System.IO.File.Exists(fileName))) 
                        System.IO.File.Delete(fileName);
                    
                 catch (Exception)  
            
        

           
</script>

更新:我现在正在使用一种新的(改进的)方法,而不是上述方法。新的涉及 HttpRuntime.Cache 并检查文件是否超过 8 小时。如果有人感兴趣,我会在这里发布。这是我的新 global.asax.cs

using System;
using System.Web;
using System.Text;
using System.IO;
using System.Xml;
using System.Web.Caching;

public partial class global : System.Web.HttpApplication 
    protected void Application_Start() 
        RemoveTemporaryFiles();
        RemoveTemporaryFilesSchedule();
    

    public void RemoveTemporaryFiles() 
        string pathTemp = "d:\\uploads\\";
        if ((pathTemp.Length > 0) && (Directory.Exists(pathTemp))) 
            foreach (string file in Directory.GetFiles(pathTemp)) 
                try 
                    FileInfo fi = new FileInfo(file);
                    if (fi.CreationTime < DateTime.Now.AddHours(-8)) 
                        File.Delete(file);
                    
                 catch (Exception)  
            
        
    

    public void RemoveTemporaryFilesSchedule() 
        HttpRuntime.Cache.Insert("RemoveTemporaryFiles", string.Empty, null, DateTime.Now.AddHours(1), Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, delegate(string id, object o, CacheItemRemovedReason cirr) 
            if (id.Equals("RemoveTemporaryFiles", StringComparison.OrdinalIgnoreCase)) 
                RemoveTemporaryFiles();
                RemoveTemporaryFilesSchedule();
            
        );
    

【讨论】:

我认为在临时目录或 ASP.NET 用户具有写入权限的另一个目录中创建基于用户会话 ID 的目录更容易。当会话结束时,只需根据会话 ID 删除用户的整个目录。 我去 sessionid 文件夹。 如果会话保存在内存中并且没有持久化到持久会话存储中,那么当你有一个应用程序回收时就有可能泄漏文件。你会丢失你的内存列表。【参考方案3】:

最好的方法是创建一个批处理文件,由 Windows 任务调度程序按您想要的时间间隔调用它。

你可以用上面的类创建一个windows服务

public class CleanUpBot


public bool KeepAlive;

private Thread _cleanUpThread;

public void Run()


_cleanUpThread = new Thread(StartCleanUp);



private void StartCleanUp()


do



// HERE THE LOGIC FOR DELETE FILES

_cleanUpThread.Join(TIME_IN_MILLISECOND);

while(KeepAlive)




注意,你也可以在 pageLoad 调用这个类,它不会影响处理时间,因为处理是在另一个线程中。只需删除 do-while 和 Thread.Join()。

【讨论】:

这些正是我试图避免的事情。首先,windows scheduler 从来都不是一个好主意(在非常许可锁定的环境中,例如在我们的客户那里,它永远不会工作),第二个 windows 服务需要安装程序,我曾经做过一次,老实说,收集的经验这正是我不想再做一次的原因。 如果您不熟悉 windows 安装程序服务,您可以在 pageLoad no 处调用 CleanUpBot 类?在第一行放这样的东西... CleanUp();在这个方法中.... private bool cleanUp() try var lastCleanUp = DateTime.Parse(Application["LastCleanUp"].ToString()); 捕捉(异常) 应用程序[“LastCleanUp”] = DateTime.Now; 最后 new CleanUpBot().Run(lastCleanUp); 【参考方案4】:
    private const string TEMPDIRPATH = @"C:\\mytempdir\";
    private const int DELETEAFTERHOURS = 8;

    private void cleanTempDir()
    
        foreach (string filePath in Directory.GetFiles(TEMPDIRPATH))
        
            FileInfo fi = new FileInfo(filePath);
            if (!(fi.LastWriteTime.CompareTo(DateTime.Now.AddHours(DELETEAFTERHOURS * -1)) <= 0)) //created or modified more than x hours ago? if not, continue to the next file
            
                continue;
            

            try
            
                File.Delete(filePath);
            
            catch (Exception)
            
                //something happened and the file probably isn't deleted. the next time give it another shot
            
        
    

上面的代码会删除 temp 目录中超过 8 小时之前创建或修改的文件。

但是我建议使用另一种方法。正如 Fredrik Johansson 建议的那样,您可以在会话结束时删除用户创建的文件。更好的是根据临时目录中用户的会话 ID 使用额外的目录。当会话结束时,您只需删除为用户创建的目录。

    private const string TEMPDIRPATH = @"C:\\mytempdir\";
    string tempDirUserPath = Path.Combine(TEMPDIRPATH, HttpContext.Current.User.Identity.Name);
    private void removeTempDirUser(string path)
    
        try
        
            Directory.Delete(path);
        
        catch (Exception)
        
            //an exception occured while deleting the directory.
        
    

【讨论】:

【参考方案5】:

我有点同意 dirk 在回答中所说的话。

这个想法是您将文件放到其中的临时文件夹是一个固定的已知位置,但我略有不同...

    每次创建文件时,将文件名添加到会话对象中的列表中(假设没有数千个,如果此列表达到给定上限,则执行下一位)

    当会话结束时,应在 global.asax 中引发 Session_End 事件。迭代列表中的所有文件并删除它们。

【讨论】:

【参考方案6】:

在 Appication_Start 上创建一个计时器,并安排计时器每 1 小时调用一次方法,并刷新超过 8 小时或 1 天或任何您需要的持续时间的文件。

【讨论】:

【参考方案7】:

您如何存储文件?如果可能,您可以采用简单的解决方案,将所有文件存储在以当前日期和时间命名的文件夹中。 然后创建一个简单的页面或 httphandler 来删除旧文件夹。您可以使用 Windows 计划或其他 cron 作业定期调用此页面。

【讨论】:

不必以当前日期和时间命名文件。文件名是 guid.tostring + ".pdf"。您可以从文件属性中读取文件创建/修改日期。是的,它们都在同一个文件夹中。【参考方案8】:

尝试使用Path.GetTempPath()。它会给你一个 Windows 临时文件夹的路径。然后由windows来清理:)

你可以在这里http://msdn.microsoft.com/en-us/library/system.io.path.gettemppath.aspx阅读更多关于该方法的信息

【讨论】:

您是否需要授予 asp.net 工作进程/应用程序池写入 temp 的权限? @James:是的,绝对是(Vista+)。此外,ASP.NET 用户需要对 temp 的读取权限、下载文件和文件枚举权限才能获取目录中的文件列表。 PS:Windows 很可能只会在重启时清理临时文件夹。如果服务器不经常崩溃/更新,我希望它不会,那是不够的。

以上是关于ASP.NET 计划删除临时文件的主要内容,如果未能解决你的问题,请参考以下文章

更改 ASP.NET 临时文件文件夹位置

临时 Asp.Net 文件的 ApplicationPoolIdentity 权限

如何删除 Access Db 的临时 ldb 文件

以中等信任度在 ASP.NET 中存储临时用户文件

ASP.NET - 如何在浏览器中显示图像而不将图像保存在临时文件中?

如何存储由我的 ASP.NET 模块操作的临时数据?