1. 概述
浏览器直接加载本地网页的时候,如果网页涉及到加载本地资源(如图片),会出现跨域的问题。Qt的Qt WebEngine模块基于Chromium项目,遇到这样的情况也会出现跨域的问题。
2. 详论
2.1. 传参
理论上,我们可以像设置chrome浏览器跨域一样(设置chrome浏览器跨域网上的资料非常多),给我们使用的Qt程序传参:
char ARG_DISABLE_WEB_SECURITY[] = "--disable-web-security"; int newArgc = argc+1+1; char** newArgv = new char*[newArgc]; for(int i=0; i<argc; i++) { newArgv[i] = argv[i]; } newArgv[argc] = ARG_DISABLE_WEB_SECURITY; newArgv[argc+1] = nullptr; QApplication myApplication(newArgc, newArgv);
Qt会将跨域参数传递到Qt WebEngine模块的Chromium内核中,从而实现跨域。
2.2. JS module
即使设置跨域,当使用JavaScript ES6 module的时候,仍然有可能会出现跨域的问题。
一个显而易见的错误提示如下:
js: Failed to load module script: The server responded with a non-JavaScript MIME type of "". Strict MIME type checking is enforced for module scripts per HTML spec.
原因在于,在Chrome浏览器的某些版本中,ES6 module的功能不支持跨域(但是require.js却可以)。现在最新版本的Chrome的跨域设置已经可以支持ES6 module,但是Qt WebEngine模块却可能是比较低的Chromium版本,从而造成使用ES6 module遇到跨域问题。通常来说,越新的Qt版本,Chromium版本也会越高。
如果还是不想要服务器环境,那么一种解决方案就是自定义URL方案:
#include <QApplication> #include <QWebEngineView> #include <QWebEngineUrlScheme> #include <QWebEngineProfile> #include <QWebEngineUrlSchemeHandler> #include <QWebEngineUrlRequestJob> #include <QFile> #include <QFile> #include <QFileInfo> #include <QMimeDatabase> class QtSchemeHandler : public QWebEngineUrlSchemeHandler { public: QtSchemeHandler(QObject *parent = nullptr):QWebEngineUrlSchemeHandler(parent) { } void requestStarted(QWebEngineUrlRequestJob *request) override { QByteArray request_method = request->requestMethod(); if(request_method != "GET") { request->fail(QWebEngineUrlRequestJob::RequestDenied); return; } QUrl request_url = request->requestUrl(); QString request_path = request_url.path(); //qDebug()<<request_url<<endl; QString application_path = "D:/"; QFile *file = new QFile(application_path + request_path); file->setParent(request); connect(request, &QObject::destroyed, file, &QFile::deleteLater); //qDebug()<<file->size()<<endl; if(!file->exists()||file->size()==0) { printf("resource '{request_path}' not found or is empty"); request->fail(QWebEngineUrlRequestJob::UrlNotFound); return; } QFileInfo file_info = QFileInfo(*file); QMimeDatabase mime_database; QMimeType mime_type = mime_database.mimeTypeForFile(file_info); request->reply(QUrl(mime_type.name()).toEncoded(), file); } }; int main(int argc, char *argv[]) { char ARG_DISABLE_WEB_SECURITY[] = "--disable-web-security"; int newArgc = argc+1+1; char** newArgv = new char*[newArgc]; for(int i=0; i<argc; i++) { newArgv[i] = argv[i]; } newArgv[argc] = ARG_DISABLE_WEB_SECURITY; newArgv[argc+1] = nullptr; qputenv("QTWEBENGINE_REMOTE_DEBUGGING", "7542"); //用于调试 QWebEngineUrlScheme scheme = QWebEngineUrlScheme("qt"); scheme.setFlags(QWebEngineUrlScheme::CorsEnabled); QWebEngineUrlScheme::registerScheme(scheme); //QApplication a(argc, argv); QApplication a(newArgc, newArgv); QWebEngineView view; QtSchemeHandler *scheme_handler = new QtSchemeHandler(); view.page()->profile()->installUrlSchemeHandler("qt", scheme_handler); view.page()->profile()->clearHttpCache(); //删除缓存 //view.load(QUrl("D:/cesium/CesiumWork/3DTilesPhotogrammetry/3DTilesPhotogrammetry.html")); view.load(QUrl("qt://local/cesium/CesiumWork/3DTilesPhotogrammetry/3DTilesPhotogrammetry.html")); view.show(); return a.exec(); }
这个方案的本质是将URL定义地址的资源给转发了一遍。但是这种方案还是有局限性,经过测试,在Qt5.15.2版本中可行,但在Qt5.12.5版本中不行。而且这样所有的资源地址都得采用这一套URL方案。
3. 建议
其实个人还是不太建议再轻易尝试使用本地网页跨域了,毕竟这一点与Web的安全性背道而驰。最好还是让网页在服务器环境下吧,出问题的可能性会小一点。