1

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

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

但是,对于足够大的文件系列,zip 文件的创建需要相当长的时间,所以我希望在后台工作线程或类似线程上发生这种情况,并向用户报告进度。

我的问题是这样的:

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

尝试在后台工作人员上打开 SaveFileDialog 将不起作用,因为它是后台线程,并且也将被视为“非用户启动”。

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

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

4

1 回答 1

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 的评论,并进行了一些进一步的挖掘,我看到 Silverlight 的安全沙箱对桌面 WPF 应用程序中未强加的文件访问施加了限制。

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

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

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

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

于 2011-07-16T18:35:49.913 回答