zoukankan      html  css  js  c++  java
  • 基于CefSharp开发浏览器(四)浏览器文件下载

    一、CefSharp文件下载分析

    查看ChromiumWebBrowser类发现cef数据下载处理在IDownloadHandler中进行,但并未找到相应的实现类,故我们需要自己实现DownloadHandler

    创建CustomDownloadHandler类并实现IDownloadHandler接口

    public class CustomDownloadHandler : IDownloadHandler
    {
        public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
            IBeforeDownloadCallback callback)
        {
            throw new System.NotImplementedException();
        }
    
        public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
            IDownloadItemCallback callback)
        {
            throw new System.NotImplementedException();
        }
    }

    IDownloadHandler中声明了两个方法,从方法命名来看 OnBeforeDownload和OnDownloadUpdated一个是在下载之前处理,一个在下载中处理

    1、首先看第一个方法OnBeforeDownload

    DownloadItem 类如下图这里给我们提供了下载文件的相关信息(文件建议名称、源地址、文件大小、当前大小等)

    查看IBeforeDownloadCallback接口定义 

    IsDisposed 判断当前下载回调对象是否已释放

    Continue 继续下载 第一个参数为下载路径、第二个数是否弹出保存对话框

    2、接下来我们看 OnDownloadUpdated

    方法中 DownloadItem 与 OnBeforeDownload中相同

    再看回调接口 IDownloadItemCallback ,提供了取消下载、暂停下载、继续下载等方法

     3、综上所述

    CefSharp下载前的设置可以在 OnBeforeDownload中进行处理

    下载的实时状态可以在 OnDownloadUpdated进行处理

    二、通知外部正在下载文件

    当文件下载时我们需要通知浏览器显示下载工具栏并更新下载状态信息

    在 CustomDownloadHandler中新增_downloadCallBackEvent事件

    private readonly Action<bool, DownloadItem> _downloadCallBackEvent;//第一个参数为true为update

    第一个参数判断在OnBeforeDownload还是在OnDownloadUpdated中执行

    public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
               IBeforeDownloadCallback callback)
    {
        if (callback.IsDisposed) return;
        _downloadCallBackEvent?.Invoke(false, downloadItem);
        downloadItem.IsInProgress = true;
        var path = GetDownloadFullPath(downloadItem.SuggestedFileName);
        callback.Continue(path, false);
    }
    
    
    public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
        IDownloadItemCallback callback)
    {
        _downloadCallBackEvent?.Invoke(true, downloadItem);
    }

    三、设计下载任务栏

    上图为Edge下载任务栏

    据图可以分为三个部分,红框部分存在一个或多个,两个绿框部分为固定部分

    创建 UserControl 【DownloadToolUc】 暂根据上图设计UI如下

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <StackPanel Grid.Column="0" x:Name="ItemsParent" Orientation="Horizontal" HorizontalAlignment="Left">
    
        </StackPanel>
        <Button Grid.Column="1" Style="{DynamicResource Button.DownloadLookAllButton}" Content="全部显示" Click="ShowAll_OnClick"/>
        <Button Grid.Column="2" Style="{DynamicResource Button.DownloadCloseButton}" Margin="5,0" Click="CloseDownloadTool_OnClick"/>
    </Grid>

    红框部分单独创建UserControl 【DownloadToolItemUc】

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Image Grid.Column="0" Margin="10,0" Source="{DynamicResource DrawingImage.File}" Width="25" Height="25"/>
        <StackPanel Grid.Column="1" HorizontalAlignment="Left">
            <TextBlock Text="{Binding FileName}"  FontSize="14" Margin="0,5,0,0" TextTrimming="CharacterEllipsis"/>
            <Grid>
                <TextBlock Margin="0,5,0,0" Visibility="Collapsed">
                    <Hyperlink FontSize="14" Foreground="{DynamicResource WebBrowserBrushes.OpenFileForeground}" Focusable="False" Click="OpenFile_OnClick">打开文件</Hyperlink>
                </TextBlock>
                <StackPanel Margin="0,5,0,0">
                    <ProgressBar Height="6"/>
                    <TextBlock FontSize="12" Margin="0,5,0,0" TextTrimming="CharacterEllipsis">
                        <Run Text="{Binding CurrentSizeStr}"/>
                        <Run Text="/"/>
                        <Run Text="{Binding TotalSizeStr}"/>
                    </TextBlock>
                </StackPanel>
            </Grid>
        </StackPanel>
        <Button Grid.Column="2" Style="{DynamicResource Button.DownloadLookAllButton}" Width="35" Height="35" Content=". . ." Padding="0,0,0,9" Margin="10,0,5,0"/>
    </Grid>

    四、下载绑定处理

    1、为DownloadToolItemUc添加ViewModel

     public class DownloadToolItemViewModel : BaseViewModel
        {
            private string _currentSizeStr;
            public string CurrentSizeStr { get => _currentSizeStr; set { _currentSizeStr = value; OnPropertyChanged("CurrentSizeStr"); } }
    
            private string _totalSizeStr;
            public string TotalSizeStr { get => _totalSizeStr; set { _totalSizeStr = value; OnPropertyChanged("TotalSizeStr"); } }
    
            private string _fileName;
            public string FileName { get => _fileName; set { _fileName = value; OnPropertyChanged("FileName"); } }
    
            public string ConvertFileSize(long size)
            {
                if (size > 1024 * 1024 * 1024)
                {
                    return $"{size / (1024 * 1024 * 1024)}G";
                }
                if (size > 1024 * 1024)
                {
                    return $"{size / (1024 * 1024)}M";
                }
                return size > 1024 ? $"{size / 1024}K" : $"{size}B";
            }
        }

    2、在DownloadToolUc中增加 DownloadFile方法

    public void DownloadFile(bool isUpdate, DownloadItem downloadItem)
    {
        if (!isUpdate)
        {
            if (_downloadDict.ContainsKey(downloadItem.Id)) return;
            var viewModel = new DownloadToolItemViewModel
            {
                FileName = downloadItem.SuggestedFileName,
            };
            _downloadDict.Add(downloadItem.Id, viewModel);
    
            this.Dispatcher.Invoke(new Action(() =>
            {
                this.Visibility = Visibility.Visible;
                var item = new DownloadToolItemUc { DataContext = viewModel };
                ItemsParent.Children.Insert(0, item);
            }));
    
        }
        else
        {
            if (!_downloadDict.ContainsKey(downloadItem.Id)) return;
            var item = _downloadDict[downloadItem.Id];
            item.CurrentSizeStr = item.ConvertFileSize(downloadItem.ReceivedBytes);
            item.TotalSizeStr = downloadItem.TotalBytes <= 0 ? "未知" : item.ConvertFileSize(downloadItem.TotalBytes);
        }
    }

    3、在创建新TabItem时建立回调事件与DownloadFile绑定

    var uc = new WebTabItemUc { ViewModel = { CurrentUrl = obj?.ToString() } };
    uc.CefWebBrowser.DownloadCallBackEvent += DownloadTool.DownloadFile;

    五、下载进度绑定

    在DownloadToolItemViewModel 中增加如下两个属性

    private double _currentSize;
    public double CurrentSize { get => _currentSize; set { _currentSize = value; OnPropertyChanged("CurrentSize"); } }
    
    private double _totalSize;
    public double TotalSize { get => _totalSize; set { _totalSize = value; OnPropertyChanged("TotalSize"); } }

    在DownloadToolItemUc控件中绑定

    <ProgressBar Height="6" Maximum="{Binding TotalSize}" Value="{Binding CurrentSize}"/>

    注意在给ViewModel赋值时downloadItem.TotalBytes可能为0,故增加TotalSize增加如下处理

    item.TotalSize = downloadItem.TotalBytes > downloadItem.ReceivedBytes? downloadItem.TotalBytes : downloadItem.ReceivedBytes;
    item.CurrentSize = downloadItem.ReceivedBytes;

    六、下载栏增加显隐动画

    任务栏的显示和隐藏有些突兀,为了显示丝滑一些,为其增加简单关键帧动画

    为 DownloadToolUc增加 RenderTransform特效 并定义Storyboard资源如下

        <UserControl.RenderTransform>
            <TransformGroup>
                <ScaleTransform/>
                <SkewTransform/>
                <RotateTransform/>
                <TranslateTransform Y="50"/>
            </TransformGroup>
        </UserControl.RenderTransform>
        <Grid Background="{DynamicResource WebBrowserBrushes.DownloadToolBackground}" x:Name="ParentGrid">
            <Grid.Resources>
                <!-- 显示下载工具栏 -->
                <Storyboard x:Key="DisplayTool" RepeatBehavior="1x">
                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="DownloadUc">
                        <EasingDoubleKeyFrame Value="0" KeyTime="00:00:01">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <QuarticEase EasingMode="EaseInOut"/>
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                    </DoubleAnimationUsingKeyFrames>
                </Storyboard>
    
                <Storyboard x:Key="HideTool" RepeatBehavior="1x">
                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="DownloadUc">
                        <EasingDoubleKeyFrame Value="50" KeyTime="00:00:01">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <QuarticEase EasingMode="EaseInOut"/>
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                    </DoubleAnimationUsingKeyFrames>
                </Storyboard>
            </Grid.Resources>

    此处我们使用TranslateTransform(平移动画) <TranslateTransform Y="50"/> 意味着初始时距离0,0点向下50

    显示下载工具栏时使纵坐标回到0点,隐藏时使纵坐标到达50

    在cs文件中增加动画启动方法

    private void InitStoryboard()
    {
        _displayToolStoryboard = (Storyboard)ParentGrid.FindResource("DisplayTool");
        _hideToolStoryboard = (Storyboard)ParentGrid.FindResource("HideTool");
    }
    
    private void DisplayTool()
    {
        _displayToolStoryboard.Begin();
    }
    
    private void HideTool()
    {
        _hideToolStoryboard.Begin();
    }

    在下载时执行DisplayTool 在关闭时执行HideTool

    七、源码地址

    gitee地址:https://gitee.com/sirius_machao/mweb-browser

  • 相关阅读:
    满20年程序员生涯-与大家分享最近7年的快速成长经历(上海市青浦区快递行业战斗7年奋斗史)
    格局 逐阶而上
    基础才是重中之重~BouncyCastle实现的DES3加密~java通用
    jenkins~Publish Over SSH实现分布式部署
    maven~为MANIFEST.MF文件添加内容
    maven~多个plugin相同phase的执行顺序
    java~jar防止反编译
    个人博客的简单通告
    SQL Server中datetimeset转换datetime类型问题浅析
    MySQL如何计算统计redo log大小
  • 原文地址:https://www.cnblogs.com/mchao/p/13935777.html
Copyright © 2011-2022 走看看