在浏览器应用程序之外的 SilverLight 中显示长时间运行的保存过程的进度

Posted

技术标签:

【中文标题】在浏览器应用程序之外的 SilverLight 中显示长时间运行的保存过程的进度【英文标题】:Displaying Progress of Long Running Save Process in SilverLight out of browser application 【发布时间】:2011-07-16 00:17:00 【问题描述】:

我有一个小的 SilverLight Out-of-Browser 应用程序,它可以从网络摄像头捕获一系列图像到 LocalStorage。然后我希望通过 Zip 文件将它们从 LocalStorage 导出到用户指定的位置。

到目前为止,如果一切都发生在主 UI 线程上,没有额外的方法,那就太简单了。

但是,对于足够大的一系列文件,zip 文件的创建需要相当长的时间,所以我希望在后台工作线程或类似线程上进行此操作,并将进度报告给用户。

我的问题是这样的:

如果我尝试在主 UI 线程上执行所有操作,ProgressBar 在保存完成之前不会更新。

尝试在后台工作人员上打开 SaveFileDialog 将无法正常工作,因为它是后台线程,并且还会被视为“非用户启动”。

无论我如何将 SaveFileDialog 中打开的 Stream 作为后台工作人员委托的一部分传递给方法,它总是更改为 CanWrite == false,我不能再使用它了。

谁有在 SilverLight 中保存大文件和报告进度的简单示例?

【问题讨论】:

【参考方案1】:

我不能声称任何有关 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));

在您的情况下,路径可以来自您在主 UI 线程上运行的 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

请注意,任何访问 UI 对象(在本例中为progressBar1)的尝试都是通过使用 Dispatcher 对象运行委托来完成的。此调度程序处理确保 UI 对象仅由 UI 线程更新的问题。这应该确保您的进度条在任务的每个片段完成后更新。


编辑:基于 OP 的 cmets,并进行了一些进一步的挖掘,我发现 Silverlight 的安全沙箱对文件访问施加了限制,而这些限制并未在桌面 WPF 应用程序中施加。

写入独立存储之外的文件系统确实需要运行 Silverlight 应用程序。这可以配置为项目属性的一部分 - 在属性的 Silverlight 选项卡上有一个“启用浏览器不足”复选框,一旦启用,它下面的“浏览器设置不足”按钮允许您打开一个更多选项对话框,其中包含“在浏览器外运行时需要提升信任”复选框。我尚未对其进行测试,但该选项听起来肯定不会在浏览器内获得更高的信任 - 因此检查代码中的安全错误并在发生信任度较低的情况时处理它可能是有意义的。

启用该设置后,您似乎可以使用普通流访问用户库中的文件,但不能访问文件系统上的其他位置。默认情况下,OpenFileDialogSaveFileDialog 类返回问题中暗示的流,但如果您愿意,它们都允许您访问文件名而不是流。打开文件时,文件名隐藏在

myOpenFileDialog.File.FullName

为了节省你似乎可以使用

mySaveFileDialog.SafeFileName

改为。

因此,以下代码可以在提升的浏览器外应用中运行:

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

这为您提供了文件访问权限和正确更新的进度条,用于处理背景中的文件。

【讨论】:

我想我将不得不考虑使用提升权限运行 SL 应用程序。问题是 SL SaveFileDialog 没有提供我可以访问的路径,只有用户指定的流 - 否则我什至不会使用 ZIP(它实际上只是所有图像的容器)我'只会将文件直接写入路径。我相信,如果我得到提升,我可以不受惩罚地访问用户库文件夹。 为此干杯——我原本会避开高空跑步,但在这种情况下,这似乎很愚蠢。然而,运行提升后,我可以完全取消整个 zip 文件,只需将图像直接写入用户的“我的图片”文件夹的文件夹中。

以上是关于在浏览器应用程序之外的 SilverLight 中显示长时间运行的保存过程的进度的主要内容,如果未能解决你的问题,请参考以下文章

开源工作流CCBPM中关于解决谷歌等浏览器silverlight的问题

如何在 Web 浏览器中从 Silverlight 应用程序捕获图像

Silverlight保存文件 提示:不允许进行文本操作,拒绝访问路径,System.IO.FileSecurityState.EnsureState()

如何重新加载在浏览器中运行 Silverlight 应用程序的默认 AppDomain?

如何从 Silverlight 应用程序中刷新浏览器页面?

VS2010中Silverlight启动设置 outofbrowser