Cef6 -- 下载支持

 今天说一下CEF的下载模块,继承CefDownloadHandler模块,重写OnBeforeDownload和OnDownloadUpdated方法。第一篇已经讲过,CEF是一个封装的浏览器控件,以接口的形式封装,也就是CEF其实是多组接口函数的集合。CEF的开发是围绕接口的开发,所有行为的实现都需要继承相应的接口,重写对应的方法,达到修改默认行为的目的。

实现下载,遇到一些问题,CSDN的答案基本都没解决,CEF的论坛很强大,有你想要的答案,强力推荐一下。

http://magpcss.org/ceforum/viewtopic.php?t=12619

CEF开发,方法重写很重要的一点,如果返回false,一般代表使用CEF的默认行为处理这次事件,返回true代表自己重新定义了这次事件的行为。

  • 继承和重写

所有的下载行为,对话框调用,右键菜单等行为都是用户行为,所以肯定在UI进程或者线程内处理。

class CustomClient : public CefClient
                   , public CefLifeSpanHandler
                   , public CefDisplayHandler
                   , public CefContextMenuHandler
                   , public CefDownloadHandler

void CustomClient::OnBeforeDownload(
	CefRefPtr<CefBrowser> browser,
	CefRefPtr<CefDownloadItem> download_item,
	const CefString& suggested_name,
	CefRefPtr<CefBeforeDownloadCallback> callback)
{
	// 这里需要传入需要保存文件的路径
	CefString url = download_item->GetURL();
	//std::string path = getDownloadPath(suggested_name);
		
	callback->Continue("", true);
}

void CustomClient::OnDownloadUpdated(
	CefRefPtr<CefBrowser> browser,
	CefRefPtr<CefDownloadItem> download_item,
	CefRefPtr<CefDownloadItemCallback> callback)
{

	if (download_item->IsComplete())
	{
		//MessageBox.Show("下载成功");
		if (browser->IsPopup() && !browser->HasDocument())
		{
			//browser->GetHost()->ParentWindowWillClose();
			browser->GetHost()->CloseBrowser(true);
		}
	}
}

 OnBeforeDownload,可以控制当前下载是否要开启(继续)或者直接退出下载。

 这里我同时继承了CefContextMenuHandler接口,重写其OnBeforeContextMenu和OnContextMenuCommand用来自定义右键菜单行为。同时继承CefLifeSpanHandler,重写其OnBeforePopup方法。为什么要继承额外这两个接口,下面分析一下CEF几种下载场景。

  • CEF下载的几种场景(同chrome浏览器的场景)

  1. 应用程序下载(或者压缩文件,镜像文件等无法在浏览器预览的文件)。CEF行为是直接弹出另存为对话框,选择路径,然后回传下载状态,到下载结束。

  2. PDF文件下载。(firefox 和chrome对PDF文件提供在线浏览功能)内部定制了pdf的行为。

  3. 图片下载。在线预览,默认不支持下载。

CEF默认的下载行为是popup新的进程窗口(多进程模式下),在该进程中处理下载行为,这种方式会有白屏的问题,下面会重点讲。

  • 解决下载页弹出空白窗口的问题

触发下载,CEF默认会调用CefLifeSpanHandler接口的OnBeforePopup方法中。我们在这里拦截到请求就可以做定制了。

bool CustomClient::OnBeforePopup(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& target_url, const CefString& target_frame_name, WindowOpenDisposition target_disposition, bool user_gesture, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, CefRefPtr<CefClient>& client, CefBrowserSettings& settings, bool* no_javascript_access)
{
	switch (target_disposition)
	{
	case WOD_NEW_FOREGROUND_TAB:
	case WOD_NEW_BACKGROUND_TAB:
	case WOD_NEW_POPUP:
	case WOD_NEW_WINDOW:

		if (!target_url.empty())
		{
			browser->GetHost()->StartDownload(target_url);
		}
		//browser->GetMainFrame()->LoadURL(target_url);
		return true; //cancel create
	}

	return false;
}

如果默认返回false,就会出现上面白屏的情况(非异常,但是有个多余的浏览器进程被创建了)。所以这里对几种模式做了过滤,禁止CEF弹出新的浏览器窗口,转而在当前浏览器加载要跳转的界面,处理如下

browser->GetMainFrame()->LoadURL(target_url);

 效果见截图(这里其实有三种情况,对应三种下载场景),直接在当前浏览器页面打开另存为对话框

对于PDF和图片,效果如下,这是CEF默认的定制化的PDF在线浏览行为,同chrome浏览器打开,而且也支持下载、打印

 

 再看下图片,图片支持比较差,默认只提供了在线预览,无法直接下载,见下图

  • 定制右键菜单,实现图片下载

所以上文的CefContextMenuHandler接口就是用来做这个工作。然后重写,新增菜单项,然后绑定响应的时间。菜单如果是中文需要转成utf8或者utf16\32等格式,否则乱码。菜单ID处理如下

void CustomClient::OnBeforeContextMenu(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefContextMenuParams> params, CefRefPtr<CefMenuModel> model)
{
	std::wstring wstr(L"另存为");
	CefString strTitle("");
	strTitle = unicode2Utf8(wstr.c_str());

	CefString url = params->GetSourceUrl();
	model->AddItem(CLIENT_ID_SHOW_DOWNLOAD_IMAGE, strTitle);

	//model->Clear();
}

bool CustomClient::OnContextMenuCommand(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefContextMenuParams> params, int command_id, EventFlags event_flags)
{
	CEF_REQUIRE_UI_THREAD();

	switch (command_id) {
	case CLIENT_ID_SHOW_DOWNLOAD_IMAGE:
		browser->GetHost()->StartDownload(params->GetSourceUrl());//DownloadImage(params->GetSourceUrl());
		return true;
	default:  // Allow default handling, if any.
		//return ExecuteTestMenu(command_id);
		break;
	}
	return false;
}
enum client_menu_ids {
	CLIENT_ID_SHOW_DOWNLOAD_IMAGE = MENU_ID_USER_FIRST,
};

触发下载操作,找了很久都没找到,最后还是在CEF论坛找到的,一共有两种方法触发下载操作

browser->GetHost()->StartDownload(params->GetSourceUrl());//DownloadImage(params->GetSourceUrl());

browser->GetHost()->DownloadImage(params->GetSourceUrl());

而且都是在browser host 上的操作。这个优劣没有比较过,上面的方法只需要传入下载的url 就可以触发下载操作,但貌似下载的中间过程无法知道(下载进度,下载速度,何时下载完成等),下面的要传入四个参数, 所以上面的中间状态全都可以获取到,貌似还可以处理缓存,可以限制图片大小等,具体没有试,这里就不瞎说了。

到这里下载和预览都支持了。最后再说一下,如何屏蔽预览和右键菜单,下载直接触发另存为操作。

屏蔽右键菜单,只需要在OnBeforeContextMenu中把model清空,见上面注释。

屏蔽预览,直接触发下载操作。同样OnBeforePopup,不加载url, 直接调用下载操作,见上面实现部分。

好了,到这里使用CEF对下载做一些定制化操作就将完了。明天见。

猜你喜欢

转载自blog.csdn.net/moyebaobei1/article/details/81587438
CEF