CEF 自定義用戶協議(scheme)實現以二進制流的方式顯示圖片、視頻、音頻


轉載:https://www.cnblogs.com/sinceret/p/10417941.html

轉載:https://stackoverflow.com/questions/48811756/registering-custom-backend-scheme-is-not-working-in-cef

轉載:https://www.twblogs.net/a/5c308e03bd9eee35b3a4d59a/zh-cn

轉載:http://www.voidcn.com/article/p-ffanxazs-bpp.html

轉載:https://magpcss.org/ceforum/viewtopic.php?f=6&t=13266

轉載:https://github.com/gabyx/ExecutionGraph/tree/3ff8b340d3ae1c87976104f0bbbda4c048e7eb2b(Github上Demo)

轉載:https://blog.csdn.net/ming19951224/article/details/81807159(解決思路參考)

轉載:https://blog.csdn.net/cdx1170776994/article/details/76571535

轉載:https://blog.csdn.net/zhangpeng_linux/article/details/85858814

轉載:https://www.cnblogs.com/himax/p/how_to_intercept_response_in_cef_using_csharp.html

在谷歌瀏覽器中點擊設置,地址欄里出現的不是普通網址,而是chrome://settings/,這個地址就是谷歌瀏覽器的自定義scheme,cef也提供了自定義協議手段。

scheme就是一種協議,比如http://helloworld.com中的http就是scheme,不過它是內置的。client://helloworld.com中的client就是一個自定義scheme。

當javascript通過XMLHttpRequest請求地址client://helloworld.com時,我們c++中實現的處理器,就會收到這個請求。

XMLHttpRequest可以通過post方法將二進制數據如arraybuffer發送到browser進程。
1.當javascript需要傳遞大量二進制數據給browser進程的c++代碼時,可以使用POST方法,
2.當javascript需要從browser進程的c++代碼拿到大量二進制數據時,可以使用GET方法。

1.CEF注冊custom scheme 來接受http請求

 在cef中我們可以通過以下接口來注冊scheme

bool CefRegisterSchemeHandlerFactory(
    const CefString& scheme_name,
    const CefString& domain_name,
    CefRefPtr<CefSchemeHandlerFactory> factory);

注冊一個自定義http scheme

CefRegisterSchemeHandlerFactory("http", "test", new MySchemeHandlerFactory());

這個scheme限定了,scheme為"http",domain為"test",只有當請求為"http://test/"開頭時,才會執行自定義c++代碼。

處理器工廠為MySchemeHandlerFactory,是我們自己實現的處理請求的c++代碼。

繼承一個工廠類MySchemeHandlerFactory : public CefSchemeHandlerFactory 

需要包含#include "include/cef_scheme.h"

 .h文件

#ifndef _MYSCHEMEHANDLERFACTORY_H_
#define _MYSCHEMEHANDLERFACTORY_H_
#include "include\cef_scheme.h"


class MySchemeHandlerFactory : public CefSchemeHandlerFactory
{
public:
    virtual CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        const CefString& scheme_name,
        CefRefPtr<CefRequest> request)
        OVERRIDE;// Return a new resource handler instance to handle the request.
        
    
private:
    IMPLEMENT_REFCOUNTING(MySchemeHandlerFactory);
};



#endif //_MYSCHEMEHANDLERFACTORY_H_

.cpp文件

#include "MySchemeHandlerFactory.h"
#include "MyResourceHandler.h"
#include "include/cef_parser.h"
#include "include/wrapper/cef_helpers.h"
#include "include/wrapper/cef_stream_resource_handler.h"
#include <shlwapi.h> 
#pragma comment(lib, "shlwapi.lib")

CefRefPtr<CefResourceHandler> MySchemeHandlerFactory::Create(CefRefPtr<CefBrowser> browser, 
    CefRefPtr<CefFrame> frame, 
    const CefString& scheme_name, 
    CefRefPtr<CefRequest> request)
{
    CEF_REQUIRE_IO_THREAD();

    CefString url = request->GetURL();
    cef_uri_unescape_rule_t unescape_rule = UU_SPACES;//處理路徑中有空格
    CefString& decoded_value = CefURIDecode(url, true, unescape_rule);

    CefURLParts urlParts;
    if (CefParseURL(url, urlParts))
    {
        std::wstring filePath = CefURIDecode(CefString(&urlParts.query),true,unescape_rule);//處理有中文路徑
        CefRefPtr<CefStreamReader> fileStream = CefStreamReader::CreateForFile(CefString(filePath));

        if (fileStream != nullptr)
        {
            // "ext" 獲取擴展名 例如:"txt"、"jpg"
            std::wstring ext;
            ext = PathFindExtension(filePath.c_str());
            ext =ext.substr(1, ext.length());
            CefString mimeType(CefGetMimeType(ext));
            //todo: Complete known mime times with web-font extensions
            if (mimeType.empty())
            {
                //mimeType = "font/" + fileExtension;
            }

            return CefRefPtr<CefStreamResourceHandler>(new CefStreamResourceHandler(mimeType, fileStream));//以二進制流的方式把文件返給js並顯示到html標簽中
        }
    }

    return new MyResourceHandler();
}

2.繼承一個資源類class MyResourceHandler : public CefResourceHandler

ProcessRequest 處理請求,允許請求返回true;取消請求返回false。當需要返回的數據就緒后(自行請求網絡或者文件就緒)或立即調用callback.Continue(),通知cef進入下一步:GetResponseHeaders。

GetResponseHeaders 設置響應頭,在這里可以設置mime-type,響應長度,其它頭等。不確定長度設置響應長度為-1。

ReadResponse 設置響應結果。可以設置響應具體內容,設置已讀取長度。cef調用完上一步后會繼續調用此方法。根據響應長度和數據就緒情況,調用此方法的次數和策略不同。

當響應長度為-1時,cef無法根據已讀長度確定是否已讀取完畢,必需根據返回值false來結束讀取。

當響應長度大於0時,cef根據每次調用得到的已讀長度,或返回值false來結束讀取。

如果數據未就緒,可以設置讀取長度為0 ,返回true,並在稍后調用callbak.Continue()通知cef調用此方法讀取響應內容。

注意:

當響應長度為-1時,必需保證此方法至少執行兩次,第一次返回true表示數據全部就緒,第二次返回false表示讀取結束。

當響應長度大於0時,設置內容和已讀長度,返回true。則此方法只執行一次。

若實際返回的響應內容,長度大於之前設置的響應總長度,則返回內容將被截取。

#ifndef _MYRESOURCEHANDLER_H_
#define _MYRESOURCEHANDLER_H_
#include <fstream>
#include <stdio.h>
#include "include\cef_resource_handler.h"

class MyResourceHandler : public CefResourceHandler
{
public:
    MyResourceHandler();

    virtual bool ProcessRequest(CefRefPtr<CefRequest> request,
        CefRefPtr<CefCallback> callback)OVERRIDE;

    virtual void GetResponseHeaders(CefRefPtr<CefResponse> response,
        int64& response_length,
        CefString& redirectUrl) OVERRIDE;

    virtual void Cancel() OVERRIDE{
        // Cancel the response...
    }

    virtual bool ReadResponse(void* data_out,
        int bytes_to_read,
        int& bytes_read,
        CefRefPtr<CefCallback> callback)
        OVERRIDE;

    virtual bool CanGetCookie(const CefCookie& cookie);
    

    virtual bool CanSetCookie(const CefCookie& cookie);
    
private:
    std::string data_;
    IMPLEMENT_REFCOUNTING(MyResourceHandler);
};
#endif//_MYRESOURCEHANDLER_H_

.cpp文件

#include "MyResourceHandler.h"

MyResourceHandler::MyResourceHandler()
{

}


bool MyResourceHandler::ProcessRequest(CefRefPtr<CefRequest> request,
    CefRefPtr<CefCallback> callback)
    {
    std::string url = request->GetURL();
    
    data_ = "hello cef";//返回到頁面中的內容
    callback->Continue();//這個一定要有
    return true;//
}

void MyResourceHandler::GetResponseHeaders(CefRefPtr<CefResponse> response,
    int64& response_length,
    CefString& redirectUrl) {

    response->SetMimeType("text/plain");
    response->SetStatus(200);
    response_length = data_.length();
}


bool MyResourceHandler::ReadResponse(void* data_out,
    int bytes_to_read,
    int& bytes_read,
    CefRefPtr<CefCallback> callback)
    {
    int size = static_cast<int>(data_.length());
    memcpy(data_out, data_.c_str(), size);
    bytes_read = size;
    return true;
}

bool MyResourceHandler::CanGetCookie(const CefCookie& cookie)
{
    return false;
}

bool MyResourceHandler::CanSetCookie(const CefCookie& cookie)
{
    return false;
}

3.在初始化cef那幾行代碼后面增加一句

CefRegisterSchemeHandlerFactory("http", "test", new MySchemeHandlerFactory());
CefSettings cSettings;
    const char* loc = "zh-CN";

    cSettings.no_sandbox = true;
    cSettings.multi_threaded_message_loop = true;
    //cSettings.single_process = false;
    cSettings.log_severity = LOGSEVERITY_DISABLE;//設置日志級別,解決安裝啟動佰卓數安后桌面出現一個debug.log文件(調試階段可以去掉)
    CefString(&cSettings.locale).FromASCII(loc);
    cef_string_from_ascii(loc, strlen(loc), &cSettings.locale);

    // Execute the secondary process, if any.
    CefInitialize(main_args, cSettings, spApp.get(), sandbox_info);
    /***************************************結束初始化cef*******************************************/
    //自定義scheme    
    CefRegisterSchemeHandlerFactory("http", "test", new MySchemeHandlerFactory());

4.CEF瀏覽器加載的靜態html

 <html>
<script>
function do_post()
{
   //alert('posting');
   var xhr  = new XMLHttpRequest();
   var zurl = "http://test?C:\\Users\\Administrator\\Desktop\\梅西.jpg";
   xhr.open("GET", zurl, true);
   xhr.responseType = 'blob';
   xhr.onload = function()
   {
    //console.log(this);
    if (this.status == 200) 
    {
        var blob = this.response;
        var img = document.createElement("img");
        img.onload = function(e) {
            window.URL.revokeObjectURL(img.src); 
        };
        //alert(blob);
        img.src = window.URL.createObjectURL(blob);
        document.body.appendChild(img); 
    }
   }
   xhr.send();
   //alert('posted');
}

function do_video()
{
   //alert('posting');
   var xhr  = new XMLHttpRequest();
   var zurl = "http://test?C:\\Users\\Administrator\\Desktop\\稻香.mp4";
   xhr.open("GET", zurl, true);
   xhr.responseType = 'blob';
   xhr.onload = function()
   {
    //console.log(this);
    if (this.status == 200) 
    {
        var blob = this.response;
        var video = document.querySelector("video");
        //alert(window.URL.createObjectURL(blob));
        video.src = window.URL.createObjectURL(blob);
  
    }
   }
   xhr.send();
   //alert('posted');
}

function do_audio()
{
   //alert('posting');
   var xhr  = new XMLHttpRequest();
   var zurl = "http://test?C:\\Users\\Administrator\\Desktop\\千年等一回.mp3";
   xhr.open("GET", zurl, true);
   xhr.responseType = 'blob';
   xhr.onload = function()
   {
    //console.log(this);
    if (this.status == 200) 
    {
        var blob = this.response;
        var audio = document.querySelector("audio");
        //alert(window.URL.createObjectURL(blob));
        audio.src = window.URL.createObjectURL(blob);
  
    }
   }
   xhr.send();
   //alert('posted');
}

</script>
<body onLoad="">
<div>
<!-- <img src="圖片路徑、地址" alt="" /> -->
<input type="button" value="加載圖片" onclick="do_post()">
<input type="button" value="加載視頻" onclick="do_video();">
<input type="button" value="加載音頻" onclick="do_audio();">
</div>
<video controls="true" ></video> 
<audio controls="true" ></audio>
<p>Hello everyone!
</p>
</body>
</html>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM