Отображение прогресса в длительном процессе сохранения в SilverLight из приложения браузера - PullRequest
1 голос
/ 16 июля 2011

У меня есть небольшое приложение SilverLight вне браузера, которое захватывает серию изображений из веб-камеры в LocalStorage.Затем я хочу экспортировать их из LocalStorage через Zip-файл в папку, указанную пользователем.

Пока что все так просто, если все происходит в основном потоке пользовательского интерфейса без дополнительных методов.

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

Моя проблема заключается в следующем:

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

Попытка открыть SaveFileDialog наФоновый рабочий не будет работать, поскольку он является фоновым потоком, и его также будут считать «не инициированным пользователем».

Независимо от того, как я передаю поток, открытый в SaveFileDialog, методу в качестве частиделегат для фонового работника, он всегда меняется на CanWrite == false, и я больше не могу его использовать.

У кого-нибудь есть простой пример saving большой файл и отчеты о прогрессе в SilverLight?

1 Ответ

1 голос
/ 16 июля 2011

Я не могу претендовать на какие-либо конкретные знания по обработке файлов в Silverlight, но вот шаблон, который я использовал бы для длинной задачи в рабочем потоке в приложении WPF. В быстром тестовом проекте Silverlight все работает нормально.

Я бы не пытался передавать потоки между потоками. Вместо этого разработайте набор параметров, в которых нуждается ваша фоновая задача, и создайте объект для передачи их вашему потоку. Пусть фоновый поток открывает файлы. Так что если вам нужна папка для поиска файлов для архивирования и выходной каталог для помещения архива, вы можете объявить:

class TaskStartupInfo
{
    public string SourceFolder { get; set; }
    public string TargetFile { get; set; }
}

Затем вы можете создать экземпляр этого класса и передать его в фоновое задание:

private void startTaskButton_Click(object sender, RoutedEventArgs e)
{
    TaskStartupInfo tsi = new TaskStartupInfo()
    {
        SourceFolder = @"C:\Some\Folder\",
        TargetFile = @"C:\AnotherFolder\data.zip" 
    };

    ThreadPool.QueueUserWorkItem(o => longRunningProcess(tsi));
}

В вашем случае пути могут быть получены из SaveFileDialog, который вы запускаете в основном потоке пользовательского интерфейса - так как этот поток не будет связан с выполнением основной части работы. Ваш метод longRunningProcess() может затем взять данные и работать с ними:

private void longRunningProcess(object o)
{
    TaskStartupInfo tsi = o as TaskStartupInfo;

    int taskLength = calculateTaskLength()

    // open any files required

    this.Dispatcher.BeginInvoke(() => { progressBar1.Value = 0; progressBar1.Maximum = taskLength; });

    for (int i = 0; i < taskLength; i++ )
    {
        doSomethingSlow();
        this.Dispatcher.BeginInvoke(() => progressBar1.Value += 1);
    }

    // close / dispose files
}

Обратите внимание, как любая попытка доступа к объекту пользовательского интерфейса (progressBar1 в данном случае) осуществляется с помощью объекта Dispatcher для запуска делегата. Этот диспетчер занимается вопросом обеспечения того, чтобы объекты пользовательского интерфейса обновлялись только потоком пользовательского интерфейса. Это должно гарантировать, что ваш индикатор выполнения обновляется после завершения каждого фрагмента задачи.


Редактировать: Основываясь на комментариях ОП и сделав некоторые дальнейшие исследования, я вижу, что песочница безопасности Silverlight накладывает ограничения на доступ к файлам, которые не налагаются в настольном приложении WPF.

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

Как только этот параметр включен, вы, похоже, сможете использовать обычные потоки для доступа к файлам в библиотеках пользователя, но не в других местах файловой системы. По умолчанию классы OpenFileDialog и SaveFileDialog возвращают потоки, как подразумевается в вопросе, но оба они позволяют вам получить доступ к имени файла, а не к потоку, если хотите. При открытии файла имя файла скрывается под

myOpenFileDialog.File.FullName

и для сохранения вы, похоже, сможете использовать

mySaveFileDialog.SafeFileName

вместо.

Таким образом, следующий код может работать в приложении Out-of-Browser с повышенными правами:

private void start_Click(object sender, RoutedEventArgs e)
{
    SaveFileDialog sfd = new SaveFileDialog();

    if (sfd.ShowDialog() != true)
    {
        return;
    }

    TaskStartupInfo tsi = new TaskStartupInfo()
    {
        SourceFolder = @"C:\Users\MyUser\Documents\Information",
        TargetFile = sfd.SafeFileName
    };

    ThreadPool.QueueUserWorkItem(o => longRunningProcess(tsi));
}

private void longRunningProcess(object o)
{
    TaskStartupInfo tsi = o as TaskStartupInfo;

    var files = Directory.EnumerateFiles(tsi.SourceFolder);

    int taskLength = files.Count();

    this.Dispatcher.BeginInvoke(() => { progressBar1.Value = 0; progressBar1.Maximum = taskLength; });

    using (StreamWriter fs = new StreamWriter(tsi.TargetFile))
    {
        foreach(string file in files)
        {
            fs.WriteLine(file);
            doSomethingSlow();
            this.Dispatcher.BeginInvoke(() => progressBar1.Value += 1);
        }
    }
}

И это дает вам как доступ к файлам, так и правильно обновленный индикатор выполнения для обработки файлов в backgrond.

...