Удалите большое количество (> 100K) файлов с c# при поддержании производительности в веб-приложении?

Я пытаюсь удалить большое количество файлов от местоположения (большим, который я имею в виду по 100 000), посредством чего действию подражают от веб-страницы. Очевидно, я мог просто использовать

string[] files = System.IO.Directory.GetFiles("path with files to delete");
foreach (var file in files) {
    IO.File.Delete(file);
}

Каталог. GetFiles http://msdn.microsoft.com/en-us/library/wz42302f.aspx

Этот метод был уже отправлен несколько раз: Как удалить все файлы и папки в каталоге? и Удалите файлы из каталога, если имя файла содержит определенное слово

Но проблема с этим методом состоит в том, что, если Вы имеете, говорят, что сто тысяч файлов, это становится проблемой производительности, поскольку это должно генерировать все filepaths сначала перед цикличным выполнением через них.

Добавленный к этому, если веб-страница ожидает ответ из метода, который выполняет это, поскольку можно предположить, что это посмотрит немного мусора!

Одна мысль, которую я имел, состояла в том, чтобы обернуть это в asychrnonous вызов веб-сервиса и когда она завершается, она ведет ответный огонь ответ на веб-страницу, чтобы сказать, что они были удалены? Возможно, поместите удалить метод в отдельный поток? Или, возможно, даже используйте отдельную пакетную обработку для выполнения удаления?

У меня есть подобная проблема при попытке считать количество файлов в каталоге - если оно содержит большое количество файлов.

Я задавался вопросом, является ли это всем небольшим излишеством? Т.е. есть ли более простой метод для контакта с этим? Любая справка ценилась бы.

15
задан Community 23 May 2017 в 11:47
поделиться

8 ответов

  1. GetFiles чрезвычайно медленно.
  2. Если вы вызываете его с веб-сайта, вы можете просто бросить новый поток, который делает этот трюк.
  3. Вызов ASP.NET AJAX, который возвращает, есть ли все еще подходящие файлы, можно использовать для проведения основных обновлений прогресса.

Ниже приведена реализация упаковки Win32 Fast Win32 для GetFiles , используйте его в сочетании с новой нитью и функцией AJAX, такую ​​как: GetFilesUnmanaged (@ "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

   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;
}
11
ответ дан 1 December 2019 в 04:17
поделиться

Делать это в отдельном потоке или отправить сообщение в очередь (может быть MSMQ?), где другое приложение (может быть служба windows) подписано на эту очередь и выполняет команды (т.е. "Удалить e:\dir*.txt") в своем собственном процессе.

Вероятно, сообщение должно просто содержать имя папки. Если вы используете что-то вроде NServiceBus и очереди транзакций, то вы можете отправить сообщение и вернуться немедленно, пока сообщение было успешно отправлено. Если на самом деле существует проблема с обработкой сообщения, то оно повторит попытку и, в конце концов, перейдет в очередь ошибок , которую вы можете просмотреть и выполнить техобслуживание.

1
ответ дан 1 December 2019 в 04:17
поделиться

Наличие более 1000 файлов в каталоге - огромная проблема.

Если вы находитесь на этапах разработки сейчас, вы должны рассмотреть возможность поделиться в ALGO , которые будут помещать файлы в случайную папку (внутри вашей корневой папки) с помощью поручителя количества файлов в этом Папка должна быть до 1024 года .

Что-то вроде

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

при этом, также убедитесь, что каждый раз, когда вы создаете файл, добавьте его в хесмап или список одновременно (путь). Периодически сериализуйте это с помощью чего-то вроде json.net к файловой системе [Sake Sake, чтобы даже если ваша услуга не удалась, вы можете вернуть список файлов из сериализованной формы).

Когда вы хотите очистить файлы или запросы среди них, сначала сделайте поиск этого hashmap или список, а затем действовать в файл. Это лучше, чем System.io.directory.getfiles

1
ответ дан 1 December 2019 в 04:17
поделиться

Сделайте это в отдельном потоке или отправьте сообщение в очередь (может быть, MSMQ ?), где другое приложение (может быть служба Windows) подписано на эту очередь и выполняет команды (например, «Delete e :\dir * .txt») в собственном процессе.

Вероятно, сообщение должно содержать только имя папки. Если вы используете что-то вроде NServiceBus и транзакционных очередей, вы можете отправить сообщение и вернуться немедленно, пока сообщение было опубликовано успешно. Если на самом деле возникла проблема с обработкой сообщения, он повторит попытку и в конечном итоге перейдет в очередь ошибок , которую можно просмотреть и выполнить техническое обслуживание.

-121--2410568-

Просто укажите одну из причин, по которой это ограничение (по крайней мере, в текущем стандарте).

При сопоставлении специализаций шаблонов компилятор сопоставляет аргументы шаблона, включая аргументы, не относящиеся к типу.

По самой своей природе плавающие значения точки не являются точными, и их реализация не определена стандартом C++. В результате, трудно решить, когда два аргументов с плавающей точкой не типа действительно совпадают:

template <float f> void foo () ;

void bar () {
    foo< (1.0/3.0) > ();
    foo< (7.0/21.0) > ();
}

Эти выражения не обязательно производят один и тот же «битовый образец» и поэтому было бы невозможно гарантировать, что они использовали одну и ту же специализацию - без специальной формулировки, чтобы охватить это.

-121--731715-

Загрузите работу в рабочий поток, а затем верните ответ пользователю.

Я бы пометил переменную приложения, чтобы сказать, что вы выполняете «большое задание удаления», чтобы остановить выполнение нескольких потоков, выполняющих одну и ту же работу. Вы могли бы затем опросить другую страницу, которая могла бы дать вам прогресс обновления количество файлов удалены до сих пор, если вы хотите?

Просто запрос, но почему так много файлов?

0
ответ дан 1 December 2019 в 04:17
поделиться

Вы можете создать простой Ajax WebMethod в вашем коде ASPX и вызовите его с помощью JavaScript.

0
ответ дан 1 December 2019 в 04:17
поделиться

Лучшим выбором (imho) будет создание отдельного процесса удаления/считывания файлов и проверка прогресса путем опроса, в противном случае могут возникнуть проблемы с таймаутами браузера.

0
ответ дан 1 December 2019 в 04:17
поделиться

Ух ты. Я думаю, что вы определенно на правильном пути, когда какой-нибудь другой сервис или сущность позаботится об удалении. При этом вы также можете предоставить методы отслеживания процесса удаления и показа результата пользователю с помощью asynch javascript.

Как говорили другие, помещение этого в другой процесс - отличная идея. Вы же не хотите, чтобы IIS перехватывал ресурсы, используя такие длительные задачи. Другая причина для этого - безопасность. Возможно, вы не захотите давать вашему рабочему процессу возможность удалять файлы с диска.

0
ответ дан 1 December 2019 в 04:17
поделиться

Убедитесь, что каждая указанная точка поддается эмпирической проверке.

Например, в игре со стрельбой в 2D не просто укажите «Оружие». Это может означать много чего. Явно укажите «Игрок может стрелять из оружия» или «Игрок может брать оружие» и т.д.

Это означает, что вы можете легко поставить конкретные цели, а затем убедитесь, что вы выполнили их .

-121--3357745-

Еще один + 1 в Subversion. Используется с различными языками, включая ColdFusion. Черепаха отлично подходит для окон, лучшие графические клиенты для Mac не бесплатны, однако.

Будет также рекомендовать против Rational Clearcase. Их клиент kludgy и есть не так близко, как много плагинов для различных ide/платформ.

-121--3227034-

Можно ли поместить все файлы в один каталог?

Если да, то почему бы просто не позвонить в Directory.Delete(последовательность, bool) на удаляемом вложенном каталоге?

Если у вас уже есть список путей к файлам, от которых вы хотите избавиться, вы можете получить лучшие результаты, переместив их в временный каталог, а затем удалив их, а не вручную.

Ура, Флориан

3
ответ дан 1 December 2019 в 04:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: