前言
前面做了一些交互,網頁是直接通過html對response進行返回的,這裏QtWebApp與傳統的web服務器不同,傳統的web服務器可以調用同級目錄相對路徑或者絕對路徑下的js,而QtWebApp的httpserver是response返回當前頁面的問題,默認是無法調用的。
爲了解決調用一些依賴的如echarts等一些js的代碼模塊引入的問題,就需要靜態文件了。
本篇解說StaticFileController,在返回的html文本中調用外部js文件,類似的,其他文件都是一樣了,只是引入的後綴名不一樣。
這裏是調用靜態文件js的
這裏是重定向測試的
如果QtWebapp無法傳遞存儲在服務器文件夾中的靜態文件,那麼它將是不完整的。StaticFileController提供了這一功能。但在使用它之前,需要在ini文件中進行一些額外的配置設置:
[files]
path=../docroot
encoding=UTF-8
maxAge=90000
cacheTime=60000
cacheSize=1000000
maxCachedFileSize=65536
- path:設置指定存儲靜態文件的基本文件夾。它是相對於配置文件的。還可以編寫絕對路徑名,如“/opt/server/docroot”或“C:/server/docroot”。
- encoding:encoding參數僅用於*.txt和*.html文件,用於告訴瀏覽器這些文件的編碼方式。如果同時需要不同的編碼,則必須創建StaticFileController的多個實例——每個編碼一個。
其他參數控制高速緩存。首先,應該知道操作系統已經緩存了文件。然而,發現Linux和Windows在處理小文件時都表現不佳。因此,建議使用應用程序內部緩存,但僅適用於小文件。 - cacheTime:cacheTime控制文件在內存中最多保存多少毫秒。值0表示,只要有足夠的空間,文件就會保留在內存中。
- cacheSize:cacheSize指定允許緩存佔用的內存量。一兆字節是一個很好的開始值。如果用戶請求的文件不在緩存中,則會刪除最舊的文件,爲新文件騰出空間。
- maxCachedFileSize:maxCachedFileSize控制緩存中單個文件的最大大小。web服務器應用程序不會緩存較大的文件。但正如所寫的那樣,操作系統可以很好地緩存大文件。事實證明,64千字節是一個很好的開始值。
- maxAge:maxAge參數的含義與cacheTime基本相同,但控制網絡瀏覽器的緩存,而不是服務器。
需要一個指向StaticFileController實例的全局指針,以便整個程序都可以訪問它。首先添加到
global.h:
#ifndef GLOBAL_H
#define GLOBAL_H
#include "httpsessionstore.h"
#include "staticfilefontroller.h"
using namespace stefanfrings;
extern HttpSessionStore* sessionStore;
extern StaticFileController* staticFileController;
#endif // GLOBAL_H
global.cpp:
#include "global.h"
HttpSessionStore* sessionStore;
StaticFileController* staticFileController;
在main.cpp中,配置StaticFileController的實例:
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QString configFileName=searchConfigFile();
// Session store
QSettings* sessionSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
sessionSettings->beginGroup("sessions");
sessionStore=new HttpSessionStore(sessionSettings,&app);
// Static file controller
QSettings* fileSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
fileSettings->beginGroup("files");
staticFileController=new StaticFileController(fileSettings,&app);
// HTTP server
QSettings* listenerSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
listenerSettings->beginGroup("listener");
new HttpListener(listenerSettings,new RequestMapper(&app),&app);
return app.exec();
}
現在可以在requestmapper.cpp中使用staticFileController:
#include "requestmapper.h"
#include "httpsession.h"
#include "global.h"
void RequestMapper::service(HttpRequest& request, HttpResponse& response) {
QByteArray path=request.getPath();
qDebug("RequestMapper: path=%s",path.data());
if (path=="/" || path=="/hello") {
helloWorldController.service(request, response);
}
else if (path=="/list") {
listDataController.service(request, response);
}
else if (path=="/login") {
loginController.service(request, response);
}
else if (path=="/cookie") {
cookieTestController.service(request, response);
}
else if (path.startsWith("/files")) {
staticFileController->service(request,response);
}
else {
response.setStatus(404,"Not found");
response.write("The URL is wrong, no such document.");
}
qDebug("RequestMapper: finished request");
}
現在創建文件夾MyFirstWebApp/docroot/files,然後創建一個名爲hello.HTML的HTML文件:
<html>
<body>
Hello World!
</body>
</html>
啓動程序並打開http://localhost:8080/files/hello.html.瀏覽器將接收該文件的內容。
可以將其他文件(圖像、css、javascript…)添加到該文件夾中,如果願意,還可以創建更多的子文件夾。
如果出現“找不到文件”錯誤,調試消息將幫助找出服務器真正試圖加載的文件。
有時想將瀏覽器重定向到另一個頁面。這通常用於需要用戶登錄的網站。如果用戶沒有登錄,他會被重定向到登錄頁面。當然,匿名用戶必須可以訪問登錄頁面本身。
requestmapper.cpp中的更改:
void RequestMapper::service(HttpRequest& request, HttpResponse& response) {
QByteArray path=request.getPath();
qDebug("RequestMapper: path=%s",path.data());
QByteArray sessionId=sessionStore->getSessionId(request,response);
if (sessionId.isEmpty() && path!="/login") {
qDebug("RequestMapper: redirect to login page");
response.redirect("/login");
return;
}
else if (path=="/login") {
...
}
else if (path=="/whatever") {
...
}
qDebug("RequestMapper: finished request");
}
HTTP服務器總是使用QByteArray而不是QString,原因很簡單:性能。整個HTTP協議都是基於8位編碼的,所以決定不浪費CPU時間,不必要地來回轉換。
但是當然可以使用Unicode。例子:
void UnicodeController::service(HttpRequest& request, HttpResponse& response) {
QString chinese=QString::fromUtf8("美麗的花朵需要重症監護");
response.setHeader("Content-Type", "text/html; charset=UTF-8");
response.write(chinese.toUtf8(),true);
}
這是谷歌翻譯(不會說中文)提供的“美麗的花朵需要重症監護”的中文翻譯。
從QString到UTF-8的轉換並不比到Latin1的轉換慢。因此,如果需要,請隨時使用Unicode。但千萬不要忘記使用QString::fromUtf8。如果只寫中文=“美麗的花朵需要重症監護“,只會得到亂碼。
準備之前的demo v1.4.0模板:
maxCachedFileSize=65536
新增靜態配置,路徑調整問exe當前的子目錄www(符合後端基本習慣)
[files]
path=../www
encoding=UTF-8
maxAge=90000
cacheTime=60000
cacheSize=1000000
必須分流,靜態文件指示的api和文件是先從處理過程然後再到靜態文件管理類的,所以有些是請求數據則需要在代碼中處理,這裏之前是沒有這樣做,可查看“入坑一”。
本Demo無法打開跳轉的staticFileUserJs,可查看“入坑二”
本Demo靜態文件無法調用js,請查看“入坑三”。
[listener]
;ip=127.0.0.1
port=8080
readTimeout=60000
maxRequestSize=16000
maxMultiPartSize=10000000
minThreads=4
maxThreads=100
cleanupInterval=60000
[logging]
fileName=../logs/httpserver.log
;fileName=/dev/stdout
minLevel=CRITICAL
bufferSize=100
maxSize=1000000
maxBackups=2
;timestampFormat=dd.MM.yyyy hh:mm:ss.zzz
timestampFormat=yyyy-MM-dd hh:mm:ss.zzz
msgFormat={timestamp} {typeNr} {type} {thread} {msg}
;QT5 supports: msgFormat={timestamp} {typeNr} {type} {thread} {msg}\n in {file} line {line} function {function}
[files]
path=../www
encoding=UTF-8
maxAge=90000
cacheTime=60000
cacheSize=1000000
maxCachedFileSize=65536
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>長沙紅胖子Qt</title>
</head>
<body>
<p>你好, 長沙紅胖子 QQ:21497936 www.hpzwl.com</p>
<p><a href="helloworld">Hello world!</a></p>
<p><a href="list">list</a></p>
<p><a href="login">login</a></p>
<p><a href="checkState">checkState</a></p>
<p><a href="staticFileUseJs.html">staticFileUseJs</a></p>
</body>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>長沙紅胖子Qt</title>
</head>
<body>
<p><a>這裏是檢測狀態Demo v1.5.0了</a></p>
<p><a id="dt1">123.567</a></p>
<p><a id="dt2">123.567</a></p>
<p><button onclick="reset()">清空</button></p>
<p><button onclick="getDt1()">獲取</button></p>
<p><button onclick="doJs()">調用js</button></p>
<script>
function reset() {
document.getElementById("dt1").innerHTML="---.---";
document.getElementById("dt2").innerHTML="---.---";
document.getElementById("dt3").innerHTML="---.---"