使用c#删除大量(>;100K)文件,同时保持web应用程序的性能?
我试图从一个位置删除大量的文件(我指的是超过100000个),从而从一个网页启动操作。显然我可以用使用c#删除大量(>;100K)文件,同时保持web应用程序的性能?,c#,file-io,file-management,C#,File Io,File Management,我试图从一个位置删除大量的文件(我指的是超过100000个),从而从一个网页启动操作。显然我可以用 string[] files = System.IO.Directory.GetFiles("path with files to delete"); foreach (var file in files) { IO.File.Delete(file); } Directory.GetFiles 此方法已发布过几次: 和 但是,这种方法的问题是,如果您有十万个文件,那么它将成为一个性
string[] files = System.IO.Directory.GetFiles("path with files to delete");
foreach (var file in files) {
IO.File.Delete(file);
}
Directory.GetFiles
此方法已发布过几次:
和
但是,这种方法的问题是,如果您有十万个文件,那么它将成为一个性能问题,因为它必须先生成所有文件路径,然后再循环通过它们
此外,如果一个网页正在等待一个方法的响应,而该方法正在执行此操作,您可以想象它看起来有点垃圾
我的一个想法是将其封装在一个非同步的web服务调用中,当调用完成时,它会向web页面发出响应,说它们已被删除?也许把delete方法放在一个单独的线程中?或者甚至可以使用单独的批处理来执行删除
当我试图计算目录中的文件数时,如果它包含大量文件,我也会遇到类似的问题
我想知道这是不是有点过分了?即,是否有更简单的方法来处理此问题?任何帮助都将不胜感激。在单独的线程中执行,或将消息发布到队列(可能?),其中另一个应用程序(可能是windows服务)订阅该队列,并在其自己的进程中执行命令(即“Delete e:\dir*.txt”)
邮件可能只包含文件夹名称。如果您使用类似于和事务性队列的东西,那么只要消息发布成功,您就可以发布消息并立即返回。如果实际处理消息时出现问题,那么它将重试,并最终进入一个您可以查看和执行维护的进程。将工作线程引导到工作线程,然后将响应返回给用户 我会标记一个应用程序变量,表示您正在执行“大删除作业”,以停止运行多个执行相同工作的线程。然后,您可以轮询另一个页面,该页面可以为您提供迄今为止删除的文件数的进度更新,如果您愿意的话 只是一个问题,为什么会有这么多文件
GetFiles
非常慢GetFiles
的快速Win32包装的实现,将其与新线程和AJAX函数结合使用,如:getfileunmanaged(@“C:\myDir”,“*.txt*)。GetEnumerator().MoveNext()
用法
Thread workerThread = new Thread(new ThreadStart((MethodInvoker)(()=>
{
foreach(var file in GetFilesUnmanaged(@"C:\myDir", "*.txt"))
File.Delete(file);
})));
workerThread.Start();
//just go on with your normal requests, the directory will be cleaned while the user can just surf around
公共静态IEnumerable GetFileUnmanaged(字符串目录,字符串筛选器)
{
返回新的FileFinder(Path.Combine(目录、筛选器))
其中(f=>(f.Attributes&FileAttributes.Normal)=FileAttributes.Normal
||(f.Attributes&FileAttributes.Archive)==FileAttributes.Archive)
.选择(s=>s.FileName);
}
}
公共类文件枚举器:IEnumerator
{
#区域互操作导入
private const int ERROR_FILE_NOT_FOUND=2;
private const int ERROR\u NO\u MORE\u FILES=18;
[DllImport(“kernel32.dll”,SetLastError=true,CharSet=CharSet.Auto)]
私有静态外部IntPtr FindFirstFile(字符串lpFileName,out WIN32_FIND_DATA lpFindFileData);
[DllImport(“kernel32.dll”,SetLastError=true,CharSet=CharSet.Auto)]
私有静态外部bool FindNextFile(安全句柄hFindFile,out WIN32_FIND_DATA lpFindFileData);
#端区
#区域数据成员
私有只读字符串\u文件名;
私人保险箱手柄(findHandle);;
私有WIN32_FIND_DATA_win32FindData;
#端区
公共文件枚举器(字符串文件名)
{
_fileName=文件名;
_findHandle=null;
_win32FindData=新的WIN32_FIND_DATA();
}
#区域IEnumerator成员
公共FoundFileData当前
{
得到
{
if(_findHandle==null)
抛出新的InvalidOperationException(“必须首先调用MoveNext());
返回新的FoundFileData(参考win32FindData);
}
}
对象IEnumerator.Current
{
获取{返回当前;}
}
公共图书馆
{
if(_findHandle==null)
{
_findHandle=新的安全文件句柄(FindFirstFile(_fileName,out _win32FindData),true);
如果(_findHandle.IsInvalid)
{
int lastError=Marshal.GetLastWin32Error();
如果(lastError==未找到错误文件)
返回false;
抛出新的Win32Exception(lastError);
}
}
其他的
{
如果(!FindNextFile(_findHandle,out _win32FindData))
{
int lastError=Marshal.GetLastWin32Error();
if(lastError==错误\u无\u更多\u文件)
返回false;
抛出新的Win32Exception(lastError);
}
}
返回true;
}
公共无效重置()
{
如果(_findHandle.IsInvalid)
返回;
_findHandle.Close();
_findHandle.setHandlesInvalid();
}
公共空间处置()
{
_findHandle.Dispose();
}
#端区
}
公共类文件查找器:IEnumerable
{
只读字符串_文件名;
公共文件查找器(字符串文件名)
{
_fileName=文件名;
}
公共IEnumerator GetEnumerator()
{
返回新的FileEnumerator(_fileName);
}
IEnumerator IEnumerable.GetEnumerator()
{
返回GetEnumerator();
}
}
公共类FoundFileData
{
公共字符串替换文件名;
公共文件属性;
公共日期时间CreationTim
public static IEnumerable<string> GetFilesUnmanaged(string directory, string filter)
{
return new FilesFinder(Path.Combine(directory, filter))
.Where(f => (f.Attributes & FileAttributes.Normal) == FileAttributes.Normal
|| (f.Attributes & FileAttributes.Archive) == FileAttributes.Archive)
.Select(s => s.FileName);
}
}
public class FilesEnumerator : IEnumerator<FoundFileData>
{
#region Interop imports
private const int ERROR_FILE_NOT_FOUND = 2;
private const int ERROR_NO_MORE_FILES = 18;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool FindNextFile(SafeHandle hFindFile, out WIN32_FIND_DATA lpFindFileData);
#endregion
#region Data Members
private readonly string _fileName;
private SafeHandle _findHandle;
private WIN32_FIND_DATA _win32FindData;
#endregion
public FilesEnumerator(string fileName)
{
_fileName = fileName;
_findHandle = null;
_win32FindData = new WIN32_FIND_DATA();
}
#region IEnumerator<FoundFileData> Members
public FoundFileData Current
{
get
{
if (_findHandle == null)
throw new InvalidOperationException("MoveNext() must be called first");
return new FoundFileData(ref _win32FindData);
}
}
object IEnumerator.Current
{
get { return Current; }
}
public bool MoveNext()
{
if (_findHandle == null)
{
_findHandle = new SafeFileHandle(FindFirstFile(_fileName, out _win32FindData), true);
if (_findHandle.IsInvalid)
{
int lastError = Marshal.GetLastWin32Error();
if (lastError == ERROR_FILE_NOT_FOUND)
return false;
throw new Win32Exception(lastError);
}
}
else
{
if (!FindNextFile(_findHandle, out _win32FindData))
{
int lastError = Marshal.GetLastWin32Error();
if (lastError == ERROR_NO_MORE_FILES)
return false;
throw new Win32Exception(lastError);
}
}
return true;
}
public void Reset()
{
if (_findHandle.IsInvalid)
return;
_findHandle.Close();
_findHandle.SetHandleAsInvalid();
}
public void Dispose()
{
_findHandle.Dispose();
}
#endregion
}
public class FilesFinder : IEnumerable<FoundFileData>
{
readonly string _fileName;
public FilesFinder(string fileName)
{
_fileName = fileName;
}
public IEnumerator<FoundFileData> GetEnumerator()
{
return new FilesEnumerator(_fileName);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class FoundFileData
{
public string AlternateFileName;
public FileAttributes Attributes;
public DateTime CreationTime;
public string FileName;
public DateTime LastAccessTime;
public DateTime LastWriteTime;
public UInt64 Size;
internal FoundFileData(ref WIN32_FIND_DATA win32FindData)
{
Attributes = (FileAttributes)win32FindData.dwFileAttributes;
CreationTime = DateTime.FromFileTime((long)
(((UInt64)win32FindData.ftCreationTime.dwHighDateTime << 32) +
(UInt64)win32FindData.ftCreationTime.dwLowDateTime));
LastAccessTime = DateTime.FromFileTime((long)
(((UInt64)win32FindData.ftLastAccessTime.dwHighDateTime << 32) +
(UInt64)win32FindData.ftLastAccessTime.dwLowDateTime));
LastWriteTime = DateTime.FromFileTime((long)
(((UInt64)win32FindData.ftLastWriteTime.dwHighDateTime << 32) +
(UInt64)win32FindData.ftLastWriteTime.dwLowDateTime));
Size = ((UInt64)win32FindData.nFileSizeHigh << 32) + win32FindData.nFileSizeLow;
FileName = win32FindData.cFileName;
AlternateFileName = win32FindData.cAlternateFileName;
}
}
/// <summary>
/// Safely wraps handles that need to be closed via FindClose() WIN32 method (obtained by FindFirstFile())
/// </summary>
public class SafeFindFileHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool FindClose(SafeHandle hFindFile);
public SafeFindFileHandle(bool ownsHandle)
: base(ownsHandle)
{
}
protected override bool ReleaseHandle()
{
return FindClose(this);
}
}
// The CharSet must match the CharSet of the corresponding PInvoke signature
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct WIN32_FIND_DATA
{
public uint dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
public UserVolumeGenerator()
{
SetNumVolumes((short)100);
SetNumSubVolumes((short)1000);
SetVolumesRoot("/var/myproj/volumes");
}
public String GenerateVolume()
{
int volume = random.nextInt(GetNumVolumes());
int subVolume = random.nextInt(GetNumSubVolumes());
return Integer.toString(volume) + "/" + Integer.toString(subVolume);
}
private static final Random random = new Random(System.currentTimeMillis());
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern IntPtr FindFirstFile(string lpFileName, ref WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool FindNextFile(IntPtr hDindFile, ref WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MashalAs(UnmanagedType.Bool]
static extern bool DeleteFile(string lpFileName)
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MashalAs(UnmanagedType.Bool]
static extern bool DeleteDirectory(string lpPathName)
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool FindClose(IntPtr hFindFile);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLAstError = true)]
static extern uint GetFileAttributes(string lpFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLAstError = true)]
static extern bool SetFileAttributes(string lpFileName, uint dwFileAttributes);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode), Serializable, BestFitMapping(false)]
internal struct WIN32_FIND_DATA
{
internal FileAttributes dwFileAttributes;
internal FILETIME ftCreationTime;
internal FILETIME ftLastAccessTime;
internal FILETIME ftLastWriteTime;
internal int nFileSizeHigh;
internal int nFileSizeLow;
internal int dwReserved0;
internal int dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
internal string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
internal string cAlternative;
}
public void RemoveDirectory(string directoryPath)
{
var path = @"\\?\UNC\" + directoryPath.Trim(@" \/".ToCharArray());
SearchAndDelete(path);
}
private void SearchAndDelete(string path)
{
var fd = new WIN32_FIND_DATA();
var found = false;
var handle = IntPtr.Zero;
var invalidHandle = new IntPtr(-1);
var fileAttributeDir = 0x00000010;
var filesToRemove = new List<string>();
try
{
handle = FindFirsFile(path + @"\*", ref fd);
if (handle == invalidHandle) return;
do
{
var current = fd.cFileName;
if (((int)fd.dwFileAttributes & fileAttributeDir) != 0)
{
if (current != "." && current != "..")
{
var newPath = Path.Combine(path, current);
SearchAndDelete(newPath);
}
}
else
{
filesToRemove.Add(Path.Combine(path, current));
}
found = FindNextFile(handle, ref fd);
} while (found);
}
finally
{
FindClose(handle);
}
try
{
object lockSource = new Object();
var exceptions = new List<Exception>();
Parallel.ForEach(filesToRemove, file, =>
{
var attrs = GetFileAttributes(file);
attrs &= ~(uint)0x00000002; // hidden
attrs &= ~(uint)0x00000001; // read-only
SetFileAttributes(file, attrs);
if (!DeleteFile(file))
{
var msg = string.Format("Cannot remove file {0}.{1}{2}", file.Replace(@"\\?\UNC", @"\"), Environment.NewLine, new Win32Exception(Marshal.GetLastWin32Error()).Message);
lock(lockSource)
{
exceptions.Add(new Exceptions(msg));
}
}
});
if (exceptions.Any())
{
throw new AggregateException(exceptions);
}
}
var dirAttr = GetFileAttributes(path);
dirAttr &= ~(uint)0x00000002; // hidden
dirAttr &= ~(uint)0x00000001; // read-only
SetfileAttributtes(path, dirAttr);
if (!RemoveDirectory(path))
{
throw new Exception(new Win32Exception(Marshal.GetLAstWin32Error()));
}
}
private void DeleteDirectoryTree(List<string> directories)
{
// group directories by depth level and order it by level descending
var data = directories.GroupBy(d => d.Split('\\'),
d => d,
(key, dirs) => new
{
Level = key,
Directories = dirs.ToList()
}).OrderByDescending(l => l.Level);
var exceptions = new List<Exception>();
var lockSource = new Object();
foreach (var level in data)
{
Parallel.ForEach(level.Directories, dir =>
{
var attrs = GetFileAttributes(dir);
attrs &= ~(uint)0x00000002; // hidden
attrs &= ~(uint)0x00000001; // read-only
SetFileAttributes(dir, attrs);
if (!RemoveDirectory(dir))
{
var msg = string.Format("Cannot remove directory {0}.{1}{2}", dir.Replace(@"\\?\UNC\", string.Empty), Environment.NewLine, new Win32Exception(Marshal.GetLastWin32Error()).Message);
lock (lockSource)
{
exceptions.Add(new Exception(msg));
}
}
});
}
if (exceptions.Any())
{
throw new AggregateException(exceptions);
}
}