ASP.NET 计划删除临时文件

5
问题:我有一个ASP.NET应用程序,它创建临时PDF文件(供用户下载)。 现在,许多用户在许多天内可以创建许多PDF,这需要大量的磁盘空间。
最好的方法是安排删除超过1天/ 8小时的文件? 最好是在asp.net应用程序本身中...
8个回答

6

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

// 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();
            }
        });
    }
}

1
我认为更容易在临时目录或其他ASP.NET用户具有写入权限的目录中创建基于用户会话ID的目录。当会话结束时,根据会话ID删除用户的整个目录即可。 - Joop
我会进入sessionid文件夹。 - Stefan Steiger
1
如果会话被保留在内存中而没有持久化到持久会话存储中,那么当应用程序重新启动时就有可能泄漏文件。您将失去您的内存列表。 - Larry Silverman

1

最佳的方法是创建一个批处理文件,由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计划程序从来不是一个好主意(在非常权限受限的环境下,比如我们客户的环境,它从来都不起作用),其次,Windows服务需要一个安装程序,我曾经做过一次,老实说,积累的经验正是我不想再做的原因。 - Stefan Steiger
如果您不熟悉Windows安装程序服务,您可以在pageLoad中调用CleanUpBot类,对吧?在第一行放置类似于这样的内容... CleanUp(); 并在此方法中...private bool cleanUp() { try { var lastCleanUp = DateTime.Parse(Application["LastCleanUp"].ToString()); } catch (Exception) { Application["LastCleanUp"] = DateTime.Now; } finally { new CleanUpBot().Run(lastCleanUp); } } - Jean-Christophe Fortin

1

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

0
使用缓存过期通知来触发文件删除:
    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 | 如何:在缓存中删除项时通知应用程序


0
    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
            }
        }
    }

上面的代码将删除临时目录中创建或修改时间超过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.
        }
    }

0

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


不必将文件命名为当前日期和时间。文件名是 guid.tostring + ".pdf"。您可以从文件属性中读取文件的创建/修改日期。是的,它们都在同一个文件夹中。 - Stefan Steiger

0
在Application_Start上创建一个计时器,并安排计时器每隔1小时调用一个方法,清除8小时或1天或任何您需要的持续时间之前的文件。

0

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

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

  1. 每次创建文件时,将文件名添加到会话对象中的列表中(假设没有成千上万个文件,如果有,当此列表达到给定容量时,请执行下一步)

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接